From beb3fad902f46870bd660030b0ff93159340b792 Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Mon, 21 Nov 2022 10:44:22 -0800 Subject: [PATCH 1/7] Use idl for eth transaction parsing --- .../browser/ethereum_provider_impl.cc | 2 +- components/brave_wallet/common/BUILD.gn | 11 +++ .../brave_wallet/common/eth_request_helper.cc | 84 ++++++++----------- .../brave_wallet/common/eth_request_helper.h | 4 +- .../common/eth_request_helper_unittest.cc | 22 ++--- .../brave_wallet/common/json_rpc_requests.idl | 18 ++++ 6 files changed, 78 insertions(+), 63 deletions(-) create mode 100644 components/brave_wallet/common/json_rpc_requests.idl diff --git a/components/brave_wallet/browser/ethereum_provider_impl.cc b/components/brave_wallet/browser/ethereum_provider_impl.cc index 8fc6519c5225..d68d9e06bbf5 100644 --- a/components/brave_wallet/browser/ethereum_provider_impl.cc +++ b/components/brave_wallet/browser/ethereum_provider_impl.cc @@ -288,7 +288,7 @@ void EthereumProviderImpl::OnGetNetworkAndDefaultKeyringInfo( std::string from; mojom::TxData1559Ptr tx_data_1559 = - ParseEthSendTransaction1559Params(normalized_json_request, &from); + ParseEthTransaction1559Params(normalized_json_request, &from); if (!tx_data_1559) { mojom::ProviderError code = mojom::ProviderError::kInternalError; std::string message = "Internal JSON-RPC error"; diff --git a/components/brave_wallet/common/BUILD.gn b/components/brave_wallet/common/BUILD.gn index 0c1860d8923f..27b262e0160a 100644 --- a/components/brave_wallet/common/BUILD.gn +++ b/components/brave_wallet/common/BUILD.gn @@ -3,6 +3,7 @@ import("//brave/components/brave_wallet/common/config.gni") import("//build/buildflag_header.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") +import("//tools/json_schema_compiler/json_schema_api.gni") import("//tools/grit/preprocess_if_expr.gni") preprocess_folder = "preprocessed" @@ -50,6 +51,7 @@ static_library("common") { ] deps = [ ":common_constants", + ":generated_json_rpc_requests", ":mojom__generator", ":solana_utils", "//brave/third_party/argon2", @@ -102,6 +104,15 @@ source_set("common_utils") { ] } +generated_types("generated_json_rpc_requests") { + sources = [ "json_rpc_requests.idl" ] + root_namespace = "brave_wallet::%(namespace)s" + deps = [ "//base" ] + visibility = [ + ":common", + ] +} + preprocess_if_expr("preprocess_mojo") { deps = [ "//brave/components/brave_wallet/common:mojom_js" ] in_folder = "$target_gen_dir" diff --git a/components/brave_wallet/common/eth_request_helper.cc b/components/brave_wallet/common/eth_request_helper.cc index 3acd23292c02..122a4ff3b300 100644 --- a/components/brave_wallet/common/eth_request_helper.cc +++ b/components/brave_wallet/common/eth_request_helper.cc @@ -18,6 +18,7 @@ #include "base/strings/stringprintf.h" #include "brave/components/brave_wallet/common/eth_address.h" #include "brave/components/brave_wallet/common/hex_utils.h" +#include "brave/components/brave_wallet/common/json_rpc_requests.h" #include "brave/components/brave_wallet/common/web3_provider_constants.h" #include "url/gurl.h" @@ -61,43 +62,28 @@ absl::optional GetParamsDict(const std::string& json) { } // This is a best effort parsing of the data -brave_wallet::mojom::TxDataPtr ValueToTxData(const base::Value& tx_value, - std::string* from_out) { +brave_wallet::mojom::TxDataPtr ValueToTxData( + const brave_wallet::json_rpc_requests::Transaction* tx, + std::string* from_out) { + CHECK(tx); CHECK(from_out); auto tx_data = brave_wallet::mojom::TxData::New(); - const base::Value::Dict* params_dict = tx_value.GetIfDict(); - if (!params_dict) - return nullptr; - const std::string* from = params_dict->FindString("from"); - if (from) - *from_out = *from; - - const std::string* to = params_dict->FindString("to"); - if (to) - tx_data->to = *to; - - const std::string* gas = params_dict->FindString("gas"); - if (gas) - tx_data->gas_limit = *gas; - - const std::string* gas_price = params_dict->FindString("gasPrice"); - if (gas_price) - tx_data->gas_price = *gas_price; - - const std::string* value = params_dict->FindString("value"); - if (value) - tx_data->value = *value; - - const std::string* data = params_dict->FindString("data"); - if (data) { - // If data is specified it's best to make sure it's valid - std::vector bytes; - if (!data->empty() && - !brave_wallet::PrefixedHexStringToBytes(*data, &bytes)) - return nullptr; - tx_data->data = bytes; - } + *from_out = tx->from; + tx_data->to = tx->to; + if (tx->gas) + tx_data->gas_limit = *tx->gas; + if (tx->gas_price) + tx_data->gas_price = *tx->gas_price; + if (tx->value) + tx_data->value = *tx->value; + + // If data is specified it's best to make sure it's valid + std::vector bytes; + if (tx->data && !tx->data->empty() && + !brave_wallet::PrefixedHexStringToBytes(*tx->data, &bytes)) + return nullptr; + tx_data->data = bytes; return tx_data; } @@ -110,7 +96,7 @@ const char kRequestJsonRPC[] = "2.0"; namespace brave_wallet { -mojom::TxDataPtr ParseEthSendTransactionParams(const std::string& json, +mojom::TxDataPtr ParseEthTransactionParams(const std::string& json, std::string* from) { CHECK(from); from->clear(); @@ -118,10 +104,13 @@ mojom::TxDataPtr ParseEthSendTransactionParams(const std::string& json, auto param_obj = GetObjectFromParamsList(json); if (!param_obj) return nullptr; - return ValueToTxData(*param_obj, from); + auto tx = brave_wallet::json_rpc_requests::Transaction::FromValue(*param_obj); + if (!tx) + return nullptr; + return ValueToTxData(tx.get(), from); } -mojom::TxData1559Ptr ParseEthSendTransaction1559Params(const std::string& json, +mojom::TxData1559Ptr ParseEthTransaction1559Params(const std::string& json, std::string* from) { CHECK(from); from->clear(); @@ -129,24 +118,21 @@ mojom::TxData1559Ptr ParseEthSendTransaction1559Params(const std::string& json, if (!param_obj) return nullptr; + auto tx = brave_wallet::json_rpc_requests::Transaction::FromValue(*param_obj); + if (!tx) + return nullptr; + auto tx_data = mojom::TxData1559::New(); - auto base_data_ret = ValueToTxData(*param_obj, from); + auto base_data_ret = ValueToTxData(tx.get(), from); if (!base_data_ret) return nullptr; tx_data->base_data = std::move(base_data_ret); - const base::Value::Dict* params_dict = param_obj->GetIfDict(); - if (!params_dict) - return nullptr; - - const std::string* max_priority_fee_per_gas = - params_dict->FindString("maxPriorityFeePerGas"); - if (max_priority_fee_per_gas) - tx_data->max_priority_fee_per_gas = *max_priority_fee_per_gas; - const std::string* max_fee_per_gas = params_dict->FindString("maxFeePerGas"); - if (max_fee_per_gas) - tx_data->max_fee_per_gas = *max_fee_per_gas; + if (tx->max_priority_fee_per_gas) + tx_data->max_priority_fee_per_gas = *tx->max_priority_fee_per_gas; + if (tx->max_fee_per_gas) + tx_data->max_fee_per_gas = *tx->max_fee_per_gas; return tx_data; } diff --git a/components/brave_wallet/common/eth_request_helper.h b/components/brave_wallet/common/eth_request_helper.h index ac131ce8d29a..f800ff2f596a 100644 --- a/components/brave_wallet/common/eth_request_helper.h +++ b/components/brave_wallet/common/eth_request_helper.h @@ -20,9 +20,9 @@ bool GetEthJsonRequestInfo(const std::string& json, std::string* method, std::string* params); -mojom::TxDataPtr ParseEthSendTransactionParams(const std::string& json, +mojom::TxDataPtr ParseEthTransactionParams(const std::string& json, std::string* from); -mojom::TxData1559Ptr ParseEthSendTransaction1559Params(const std::string& json, +mojom::TxData1559Ptr ParseEthTransaction1559Params(const std::string& json, std::string* from); bool ShouldCreate1559Tx(mojom::TxData1559Ptr tx_data_1559, bool network_supports_eip1559, diff --git a/components/brave_wallet/common/eth_request_helper_unittest.cc b/components/brave_wallet/common/eth_request_helper_unittest.cc index 08006607bab8..29260e931541 100644 --- a/components/brave_wallet/common/eth_request_helper_unittest.cc +++ b/components/brave_wallet/common/eth_request_helper_unittest.cc @@ -23,8 +23,8 @@ TEST(EthRequestHelperUnitTest, CommonParseErrors) { "[[]]", "[0]"}); for (const auto& error_case : error_cases) { std::string from; - EXPECT_FALSE(ParseEthSendTransactionParams(error_case, &from)); - EXPECT_FALSE(ParseEthSendTransaction1559Params(error_case, &from)); + EXPECT_FALSE(ParseEthTransactionParams(error_case, &from)); + EXPECT_FALSE(ParseEthTransaction1559Params(error_case, &from)); std::string address; std::string message; EXPECT_FALSE(ParseEthSignParams(error_case, &address, &message)); @@ -48,7 +48,7 @@ TEST(EthRequestHelperUnitTest, CommonParseErrors) { } } -TEST(EthRequestHelperUnitTest, ParseEthSendTransactionParams) { +TEST(EthRequestHelperUnitTest, ParseEthTransactionParams) { std::string json( R"({ "params": [{ @@ -62,7 +62,7 @@ TEST(EthRequestHelperUnitTest, ParseEthSendTransactionParams) { }] })"); std::string from; - mojom::TxDataPtr tx_data = ParseEthSendTransactionParams(json, &from); + mojom::TxDataPtr tx_data = ParseEthTransactionParams(json, &from); ASSERT_TRUE(tx_data); EXPECT_EQ(from, "0x7f84E0DfF3ffd0af78770cF86c1b1DdFF99d51C8"); EXPECT_EQ(tx_data->to, "0x7f84E0DfF3ffd0af78770cF86c1b1DdFF99d51C7"); @@ -73,7 +73,7 @@ TEST(EthRequestHelperUnitTest, ParseEthSendTransactionParams) { EXPECT_TRUE(tx_data->nonce.empty()); // Should be ignored. } -TEST(EthResponseHelperUnitTest, ParseEthSendTransaction1559Params) { +TEST(EthResponseHelperUnitTest, ParseEthTransaction1559Params) { std::string json( R"({ "params": [{ @@ -88,7 +88,7 @@ TEST(EthResponseHelperUnitTest, ParseEthSendTransaction1559Params) { }] })"); std::string from; - mojom::TxData1559Ptr tx_data = ParseEthSendTransaction1559Params(json, &from); + mojom::TxData1559Ptr tx_data = ParseEthTransaction1559Params(json, &from); ASSERT_TRUE(tx_data); EXPECT_EQ(from, "0x7f84E0DfF3ffd0af78770cF86c1b1DdFF99d51C8"); EXPECT_EQ(tx_data->base_data->to, @@ -111,7 +111,7 @@ TEST(EthResponseHelperUnitTest, ParseEthSendTransaction1559Params) { "data": "0x010203" }] })"; - tx_data = ParseEthSendTransaction1559Params(json, &from); + tx_data = ParseEthTransaction1559Params(json, &from); ASSERT_TRUE(tx_data); EXPECT_EQ(from, "0x7f84E0DfF3ffd0af78770cF86c1b1DdFF99d51C8"); EXPECT_EQ(tx_data->base_data->to, @@ -170,7 +170,7 @@ TEST(EthResponseHelperUnitTest, ShouldCreate1559Tx) { }] })"); std::string from; - auto tx_data = ParseEthSendTransaction1559Params(json, &from); + auto tx_data = ParseEthTransaction1559Params(json, &from); ASSERT_TRUE(tx_data); EXPECT_TRUE(ShouldCreate1559Tx(tx_data.Clone(), @@ -210,7 +210,7 @@ TEST(EthResponseHelperUnitTest, ShouldCreate1559Tx) { }] })"; - tx_data = ParseEthSendTransaction1559Params(json, &from); + tx_data = ParseEthTransaction1559Params(json, &from); ASSERT_TRUE(tx_data); EXPECT_TRUE(ShouldCreate1559Tx(tx_data.Clone(), true /* network_supports_eip1559 */, @@ -230,7 +230,7 @@ TEST(EthResponseHelperUnitTest, ShouldCreate1559Tx) { "nonce": "0x01" }] })"; - tx_data = ParseEthSendTransaction1559Params(json, &from); + tx_data = ParseEthTransaction1559Params(json, &from); ASSERT_TRUE(tx_data); EXPECT_FALSE(ShouldCreate1559Tx(tx_data.Clone(), true /* network_supports_eip1559 */, @@ -248,7 +248,7 @@ TEST(EthResponseHelperUnitTest, ShouldCreate1559Tx) { "data": "0x010203" }] })"; - tx_data = ParseEthSendTransaction1559Params(json, &from); + tx_data = ParseEthTransaction1559Params(json, &from); ASSERT_TRUE(tx_data); EXPECT_TRUE(ShouldCreate1559Tx(tx_data.Clone(), true, account_infos, from)); EXPECT_TRUE(ShouldCreate1559Tx(tx_data.Clone(), true, account_infos, diff --git a/components/brave_wallet/common/json_rpc_requests.idl b/components/brave_wallet/common/json_rpc_requests.idl new file mode 100644 index 000000000000..d5f80a7114af --- /dev/null +++ b/components/brave_wallet/common/json_rpc_requests.idl @@ -0,0 +1,18 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +namespace json_rpc_requests { + dictionary Transaction { + DOMString from; + DOMString to; + DOMString? gas; + DOMString? gasPrice; + DOMString? value; + DOMString? data; + DOMString? nonce; + DOMString? maxPriorityFeePerGas; + DOMString? maxFeePerGas; + }; +}; From 5a32b3fb7d8d7b3f6cadbe6a359eb7d1abccb66d Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Fri, 2 Dec 2022 09:35:11 -0800 Subject: [PATCH 2/7] Add eth_sendRawTransaction --- .../browser/ethereum_provider_impl.cc | 29 +++++++++++++++++++ .../browser/ethereum_provider_impl.h | 5 ++++ .../brave_wallet/common/eth_request_helper.cc | 22 ++++++++++++-- .../brave_wallet/common/eth_request_helper.h | 7 +++-- .../common/eth_request_helper_unittest.cc | 27 +++++++++++++++++ .../common/web3_provider_constants.h | 1 + 6 files changed, 87 insertions(+), 4 deletions(-) diff --git a/components/brave_wallet/browser/ethereum_provider_impl.cc b/components/brave_wallet/browser/ethereum_provider_impl.cc index d68d9e06bbf5..23b5d7e8ed23 100644 --- a/components/brave_wallet/browser/ethereum_provider_impl.cc +++ b/components/brave_wallet/browser/ethereum_provider_impl.cc @@ -1017,6 +1017,19 @@ void EthereumProviderImpl::CommonRequestOrSendAsync(base::ValueView input_value, weak_factory_.GetWeakPtr(), std::move(callback), std::move(id), normalized_json_request, delegate_->GetOrigin())); + } else if (method == kEthSendRawTransaction) { + std::string signed_transaction; + if (!ParseEthSendRawTransactionParams(normalized_json_request, + &signed_transaction)) { + SendErrorOnRequest(error, error_message, std::move(callback), + std::move(id)); + return; + } + json_rpc_service_->SendRawTransaction( + signed_transaction, + base::BindOnce(&EthereumProviderImpl::OnSendRawTransaction, + weak_factory_.GetWeakPtr(), std::move(callback), + std::move(id))); } else if (method == kEthSign || method == kPersonalSign) { std::string address; std::string message; @@ -1529,4 +1542,20 @@ void EthereumProviderImpl::AddSuggestToken(mojom::BlockchainTokenPtr token, delegate_->ShowPanel(); } +void EthereumProviderImpl::OnSendRawTransaction( + RequestCallback callback, + base::Value id, + const std::string& tx_hash, + mojom::ProviderError error, + const std::string& error_message) { + base::Value formed_response; + if (error != mojom::ProviderError::kSuccess) { + formed_response = GetProviderErrorDictionary(error, error_message); + } else { + formed_response = base::Value(tx_hash); + } + std::move(callback).Run(std::move(id), std::move(formed_response), + error != mojom::ProviderError::kSuccess, "", false); +} + } // namespace brave_wallet diff --git a/components/brave_wallet/browser/ethereum_provider_impl.h b/components/brave_wallet/browser/ethereum_provider_impl.h index 6c0d38867926..575eaead5f49 100644 --- a/components/brave_wallet/browser/ethereum_provider_impl.h +++ b/components/brave_wallet/browser/ethereum_provider_impl.h @@ -346,6 +346,11 @@ class EthereumProviderImpl final const url::Origin& origin, RequestPermissionsError error, const absl::optional>& allowed_accounts); + void OnSendRawTransaction(RequestCallback callback, + base::Value id, + const std::string& tx_hash, + mojom::ProviderError error, + const std::string& error_message); raw_ptr host_content_settings_map_ = nullptr; std::unique_ptr delegate_; diff --git a/components/brave_wallet/common/eth_request_helper.cc b/components/brave_wallet/common/eth_request_helper.cc index 122a4ff3b300..719a07333f55 100644 --- a/components/brave_wallet/common/eth_request_helper.cc +++ b/components/brave_wallet/common/eth_request_helper.cc @@ -97,7 +97,7 @@ const char kRequestJsonRPC[] = "2.0"; namespace brave_wallet { mojom::TxDataPtr ParseEthTransactionParams(const std::string& json, - std::string* from) { + std::string* from) { CHECK(from); from->clear(); @@ -111,7 +111,7 @@ mojom::TxDataPtr ParseEthTransactionParams(const std::string& json, } mojom::TxData1559Ptr ParseEthTransaction1559Params(const std::string& json, - std::string* from) { + std::string* from) { CHECK(from); from->clear(); auto param_obj = GetObjectFromParamsList(json); @@ -660,4 +660,22 @@ bool ParseRequestPermissionsParams( return true; } +bool ParseEthSendRawTransactionParams(const std::string& json, + std::string* signed_transaction) { + if (!signed_transaction) + return false; + + auto list = GetParamsList(json); + if (!list || list->size() != 1) + return false; + + const std::string* signed_tx_str = (*list)[0].GetIfString(); + if (!signed_tx_str) + return false; + + *signed_transaction = *signed_tx_str; + + return true; +} + } // namespace brave_wallet diff --git a/components/brave_wallet/common/eth_request_helper.h b/components/brave_wallet/common/eth_request_helper.h index f800ff2f596a..e6cb93881277 100644 --- a/components/brave_wallet/common/eth_request_helper.h +++ b/components/brave_wallet/common/eth_request_helper.h @@ -21,9 +21,9 @@ bool GetEthJsonRequestInfo(const std::string& json, std::string* params); mojom::TxDataPtr ParseEthTransactionParams(const std::string& json, - std::string* from); + std::string* from); mojom::TxData1559Ptr ParseEthTransaction1559Params(const std::string& json, - std::string* from); + std::string* from); bool ShouldCreate1559Tx(mojom::TxData1559Ptr tx_data_1559, bool network_supports_eip1559, const std::vector& account_infos, @@ -72,6 +72,9 @@ bool ParseRequestPermissionsParams( const std::string& json, std::vector* restricted_methods); +bool ParseEthSendRawTransactionParams(const std::string& json, + std::string* signed_transaction); + } // namespace brave_wallet #endif // BRAVE_COMPONENTS_BRAVE_WALLET_COMMON_ETH_REQUEST_HELPER_H_ diff --git a/components/brave_wallet/common/eth_request_helper_unittest.cc b/components/brave_wallet/common/eth_request_helper_unittest.cc index 29260e931541..0b172ac939de 100644 --- a/components/brave_wallet/common/eth_request_helper_unittest.cc +++ b/components/brave_wallet/common/eth_request_helper_unittest.cc @@ -10,6 +10,7 @@ #include "base/base64.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "brave/components/brave_wallet/common/eth_request_helper.h" #include "brave/components/brave_wallet/common/hex_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -1142,4 +1143,30 @@ TEST(EthResponseHelperUnitTest, ParseRequestPermissionsParams) { ParseRequestPermissionsParams("{ params: [] }", &restricted_methods)); } +TEST(EthResponseHelperUnitTest, ParseEthSendRawTransaction) { + std::string raw_transaction; + constexpr char expected_raw_transaction[] = + "0xf86c0c8525f38e9e0082520894cb08bd29e330594182a05a062441ccdb348aae658801" + "6345785d8a0000802ea0b9534f8e424fd28eecb16cd771b577df6a933ff58ac8c41786f0" + "2dcba0b632c1a039d0379cdbfb54cfd1894de3bb5c0583a11bc79abf19b6961e948e2127" + "f47bec"; + + std::string json = base::StringPrintf( + R"({ + "method": "eth_sendRawTransaction", + "params": ["%s"] + })", + expected_raw_transaction); + EXPECT_TRUE(ParseEthSendRawTransactionParams(json, &raw_transaction)); + EXPECT_EQ(raw_transaction, expected_raw_transaction); + EXPECT_FALSE(ParseEthSendRawTransactionParams(json, nullptr)); + EXPECT_FALSE(ParseEthSendRawTransactionParams("", &raw_transaction)); + EXPECT_FALSE( + ParseEthSendRawTransactionParams("{params: []}", &raw_transaction)); + EXPECT_FALSE( + ParseEthSendRawTransactionParams("{params: [123]}", &raw_transaction)); + EXPECT_FALSE(ParseEthSendRawTransactionParams("{params: {\"0x123\"}}", + &raw_transaction)); +} + } // namespace brave_wallet diff --git a/components/brave_wallet/common/web3_provider_constants.h b/components/brave_wallet/common/web3_provider_constants.h index f601cc2143fa..9c5c7a7f1ba6 100644 --- a/components/brave_wallet/common/web3_provider_constants.h +++ b/components/brave_wallet/common/web3_provider_constants.h @@ -32,6 +32,7 @@ constexpr char kEthAccounts[] = "eth_accounts"; constexpr char kEthCoinbase[] = "eth_coinbase"; constexpr char kEthRequestAccounts[] = "eth_requestAccounts"; constexpr char kEthSendTransaction[] = "eth_sendTransaction"; +constexpr char kEthSendRawTransaction[] = "eth_sendRawTransaction"; constexpr char kEthGetBlockByNumber[] = "eth_getBlockByNumber"; constexpr char kEthBlockNumber[] = "eth_blockNumber"; constexpr char kEthSign[] = "eth_sign"; From f889dceffc3e361d7e1d0ecfc575008468bed689 Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Fri, 2 Dec 2022 09:35:28 -0800 Subject: [PATCH 3/7] Add eth_signTransaction --- .../wallet_notification_service_unittest.cc | 10 +- .../browser/brave_wallet_constants.h | 6 + .../browser/eip1559_transaction.cc | 49 ++++--- .../browser/eip1559_transaction.h | 8 + .../browser/eip1559_transaction_unittest.cc | 67 +++++---- .../browser/eip2930_transaction.cc | 47 +++--- .../browser/eip2930_transaction.h | 7 + .../browser/eip2930_transaction_unittest.cc | 28 ++-- .../browser/eth_pending_tx_tracker.cc | 6 + .../brave_wallet/browser/eth_transaction.cc | 34 +++-- .../brave_wallet/browser/eth_transaction.h | 10 +- .../browser/eth_transaction_unittest.cc | 57 ++++---- .../brave_wallet/browser/eth_tx_manager.cc | 52 ++++--- .../brave_wallet/browser/eth_tx_manager.h | 3 + .../browser/eth_tx_manager_unittest.cc | 138 ++++++++++-------- .../brave_wallet/browser/eth_tx_meta.cc | 6 +- components/brave_wallet/browser/eth_tx_meta.h | 3 + .../browser/eth_tx_meta_unittest.cc | 16 +- .../browser/eth_tx_state_manager.cc | 5 + .../browser/eth_tx_state_manager_unittest.cc | 36 ++++- .../browser/ethereum_keyring_unittest.cc | 16 +- .../browser/ethereum_provider_impl.cc | 39 ++++- .../browser/ethereum_provider_impl.h | 2 + components/brave_wallet/common/BUILD.gn | 6 +- .../brave_wallet/common/brave_wallet.mojom | 5 +- .../common/web3_provider_constants.h | 1 + .../brave_wallet_ui/common/async/lib.ts | 4 +- .../brave_wallet_ui/common/constants/mocks.ts | 4 +- .../portfolio-transaction-item/index.tsx | 1 + .../extension/post-confirmation/index.tsx | 7 +- .../index.tsx | 4 +- .../submitted_or_signed.stories.tsx} | 10 +- .../submitted_or_signed.style.ts} | 6 +- .../submitted_or_signed.tsx} | 21 ++- .../panel/async/wallet_panel_async_handler.ts | 2 +- components/brave_wallet_ui/stories/locale.ts | 1 + .../mock-data/mock-transaction-info.ts | 4 +- .../stories/wallet-extension-panels.tsx | 28 +++- components/brave_wallet_ui/utils/tx-utils.ts | 2 + components/resources/wallet_strings.grdp | 3 + 40 files changed, 492 insertions(+), 262 deletions(-) rename components/brave_wallet_ui/components/extension/post-confirmation/{submitted => submitted_or_signed}/index.tsx (70%) rename components/brave_wallet_ui/components/extension/post-confirmation/{submitted/submitted.stories.tsx => submitted_or_signed/submitted_or_signed.stories.tsx} (81%) rename components/brave_wallet_ui/components/extension/post-confirmation/{submitted/submitted.style.ts => submitted_or_signed/submitted_or_signed.style.ts} (81%) rename components/brave_wallet_ui/components/extension/post-confirmation/{submitted/submitted.tsx => submitted_or_signed/submitted_or_signed.tsx} (65%) diff --git a/browser/brave_wallet/notifications/wallet_notification_service_unittest.cc b/browser/brave_wallet/notifications/wallet_notification_service_unittest.cc index 5bd7623d6f5f..2591583a3174 100644 --- a/browser/brave_wallet/notifications/wallet_notification_service_unittest.cc +++ b/browser/brave_wallet/notifications/wallet_notification_service_unittest.cc @@ -55,11 +55,11 @@ class WalletNotificationServiceUnitTest : public testing::Test { } bool WasNotificationDisplayedOnStatusChange(mojom::TransactionStatus status) { - std::unique_ptr tx = - std::make_unique(*EthTransaction::FromTxData( - mojom::TxData::New("0x01", "0x4a817c800", "0x5208", - "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()))); + std::unique_ptr tx = std::make_unique( + *EthTransaction::FromTxData(mojom::TxData::New( + "0x01", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt))); EthTxMeta meta(std::move(tx)); meta.set_status(status); notification_service_->OnTransactionStatusChanged(meta.ToTransactionInfo()); diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index 421377473035..150b846d955c 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -568,6 +568,8 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { IDS_BRAVE_WALLET_TRANSACTION_STATUS_ERROR}, {"braveWalletTransactionStatusDropped", IDS_BRAVE_WALLET_TRANSACTION_STATUS_DROPPED}, + {"braveWalletTransactionStatusSigned", + IDS_BRAVE_WALLET_TRANSACTION_STATUS_SIGNED}, {"braveWalletRecentTransactions", IDS_BRAVE_WALLET_RECENT_TRANSACTIONS}, {"braveWalletTransactionDetails", IDS_BRAVE_WALLET_TRANSACTION_DETAILS}, {"braveWalletTransactionDetailDate", @@ -725,6 +727,10 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { IDS_BRAVE_WALLET_TRANSACTION_SUBMITTED_TITLE}, {"braveWalletTransactionSubmittedDescription", IDS_BRAVE_WALLET_TRANSACTION_SUBMITTED_DESCRIPTION}, + {"braveWalletTransactionSignedTitle", + IDS_BRAVE_WALLET_TRANSACTION_SIGNED_TITLE}, + {"braveWalletTransactionSignedDescription", + IDS_BRAVE_WALLET_TRANSACTION_SIGNED_DESCRIPTION}, {"braveWalletTransactionFailedHeaderTitle", IDS_BRAVE_WALLET_TRANSACTION_FAILED_HEADER_TITLE}, {"braveWalletTransactionFailedTitle", diff --git a/components/brave_wallet/browser/eip1559_transaction.cc b/components/brave_wallet/browser/eip1559_transaction.cc index 25111e39d346..48fd415efca9 100644 --- a/components/brave_wallet/browser/eip1559_transaction.cc +++ b/components/brave_wallet/browser/eip1559_transaction.cc @@ -270,27 +270,14 @@ std::string Eip1559Transaction::GetSignedTransaction() const { DCHECK(IsSigned()); DCHECK(nonce_); - base::Value::List list; - list.Append(RLPUint256ToBlob(chain_id_)); - list.Append(RLPUint256ToBlob(nonce_.value())); - list.Append(RLPUint256ToBlob(max_priority_fee_per_gas_)); - list.Append(RLPUint256ToBlob(max_fee_per_gas_)); - list.Append(RLPUint256ToBlob(gas_limit_)); - list.Append(base::Value::BlobStorage(to_.bytes())); - list.Append(RLPUint256ToBlob(value_)); - list.Append(base::Value(data_)); - list.Append(base::Value(AccessListToValue(access_list_))); - list.Append(RLPUint256ToBlob(v_)); - list.Append(base::Value(r_)); - list.Append(base::Value(s_)); - - std::vector result; - result.push_back(type_); + return ToHex(Serialize()); +} - const std::string rlp_msg = RLPEncode(base::Value(std::move(list))); - result.insert(result.end(), rlp_msg.begin(), rlp_msg.end()); +std::string Eip1559Transaction::GetTransactionHash() const { + DCHECK(IsSigned()); + DCHECK(nonce_); - return ToHex(result); + return ToHex(KeccakHash(Serialize())); } base::Value::Dict Eip1559Transaction::ToValue() const { @@ -331,4 +318,28 @@ uint256_t Eip1559Transaction::GetUpfrontCost(uint256_t block_base_fee) const { return gas_limit_ * gas_price + value_; } +std::vector Eip1559Transaction::Serialize() const { + base::Value::List list; + list.Append(RLPUint256ToBlob(chain_id_)); + list.Append(RLPUint256ToBlob(nonce_.value())); + list.Append(RLPUint256ToBlob(max_priority_fee_per_gas_)); + list.Append(RLPUint256ToBlob(max_fee_per_gas_)); + list.Append(RLPUint256ToBlob(gas_limit_)); + list.Append(base::Value::BlobStorage(to_.bytes())); + list.Append(RLPUint256ToBlob(value_)); + list.Append(base::Value(data_)); + list.Append(base::Value(AccessListToValue(access_list_))); + list.Append(RLPUint256ToBlob(v_)); + list.Append(base::Value(r_)); + list.Append(base::Value(s_)); + + std::vector result; + result.push_back(type_); + + const std::string rlp_msg = RLPEncode(base::Value(std::move(list))); + result.insert(result.end(), rlp_msg.begin(), rlp_msg.end()); + + return result; +} + } // namespace brave_wallet diff --git a/components/brave_wallet/browser/eip1559_transaction.h b/components/brave_wallet/browser/eip1559_transaction.h index ab541e073807..d5b7c3bc35ed 100644 --- a/components/brave_wallet/browser/eip1559_transaction.h +++ b/components/brave_wallet/browser/eip1559_transaction.h @@ -72,6 +72,11 @@ class Eip1559Transaction : public Eip2930Transaction { // signatureS]) std::string GetSignedTransaction() const override; + // keccacak(0x02 || rlp([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, + // gasLimit, destination, value, data, accessList, signatureYParity, + // signatureR,signatureS])) + std::string GetTransactionHash() const override; + base::Value::Dict ToValue() const override; uint256_t GetUpfrontCost(uint256_t block_base_fee = 0) const override; @@ -93,6 +98,9 @@ class Eip1559Transaction : public Eip2930Transaction { // Gas estimation result GasEstimation gas_estimation_; + + private: + std::vector Serialize() const; }; } // namespace brave_wallet diff --git a/components/brave_wallet/browser/eip1559_transaction_unittest.cc b/components/brave_wallet/browser/eip1559_transaction_unittest.cc index a0663cbb4a0a..2ef739311d9e 100644 --- a/components/brave_wallet/browser/eip1559_transaction_unittest.cc +++ b/components/brave_wallet/browser/eip1559_transaction_unittest.cc @@ -34,7 +34,7 @@ TEST(Eip1559TransactionUnitTest, GetMessageToSign) { *Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x00", "0x00", "0x00", "0x0101010101010101010101010101010101010101", - "0x00", data), + "0x00", data, false, absl::nullopt), "0x04", "0x0", "0x0", nullptr)); ASSERT_EQ(tx.type(), 2); auto* access_list = tx.access_list(); @@ -51,7 +51,7 @@ TEST(Eip1559TransactionUnitTest, GetMessageToSign) { "fa81814f7dd57bad435657a05eabdba2815f41e3f15ddd6139027e7db56b0dea"); } -TEST(Eip1559TransactionUnitTest, GetSignedTransaction) { +TEST(Eip1559TransactionUnitTest, GetSignedTransactionAndHash) { const struct { const char* nonce; const char* value; @@ -59,58 +59,70 @@ TEST(Eip1559TransactionUnitTest, GetSignedTransaction) { const char* max_priority_fee_per_gas; const char* max_fee_per_gas; const char* signed_tx; + const char* hash; } cases[] = { {"0x333", "0x2933BC9", "0x8AE0", "0x1284D", "0x1D97C", "0x02f86e048203338301284d8301d97c828ae094000000000000000000000000000" "000000000aaaa8402933bc980c080a00f924cb68412c8f1cfd74d9b581c71eeaf94ff" "f6abdde3e5b02ca6b2931dcf47a07dd1c50027c3e31f8b565e25ce68a5072110f61fce5" - "eee81b195dd51273c2f83"}, + "eee81b195dd51273c2f83", + "0x2e564c87eb4b40e7f469b2eec5aa5d18b0b46a24e8bf0919439cfb0e8fcae446"}, {"0x161", "0x3B08B33", "0x7F51", "0x97C2", "0x21467", "0x02f86d048201618297c283021467827f519400000000000000000000000000000" "0000000aaaa8403b08b3380c080a08caf712f72489da6f1a634b651b4b1c7d9be7d1e" "8d05ea76c1eccee3bdfb86a5a06aecc106f588ce51e112f5e9ea7aba3e089dc75117188" - "21d0e0cd52f52af4e45"}, + "21d0e0cd52f52af4e45", + "0xfc638a8bd28e117cc475935979b36f272cc792ee9adde78ed66df16a72a8cdba"}, {"0x3D9", "0x1F06571", "0x10BBD", "0x10349", "0x213A1", "0x02f86f048203d983010349830213a183010bbd940000000000000000000000000" "00000000000aaaa8401f0657180c001a08c03a86e85789ee9a1b42fa0a86d316fca26" "2694f8c198df11f194678c2c2d35a028f8e7de02b35014a17b6d28ff8c7e7be6860e726" - "5ac162fb721f1aeae75643c"}, + "5ac162fb721f1aeae75643c", + "0x8b83d04ac346cad8cb062ec81133cede02c103c34b7776e591b430725aa5b0db"}, {"0x26F", "0x14A5987", "0xE17D", "0x1219C", "0x13D15", "0x02f86e0482026f8301219c83013d1582e17d94000000000000000000000000000" "000000000aaaa84014a598780c001a0b87c4c8c505d2d692ac77ba466547e79dd60fe" "7ecd303d520bf6e8c7085e3182a06dc7d00f5e68c3f3ebe8ae35a90d46051afde620ac1" - "2e43cae9560a29b13e7fb"}, + "2e43cae9560a29b13e7fb", + "0xe45dc03bc114fd3fee8c4d6f3e84b82811e741eeaca0ca5b982a50941be215b3"}, {"0x3CC", "0x5A2EC37", "0xFEE6", "0xA72E", "0x1942A", "0x02f86d048203cc82a72e8301942a82fee69400000000000000000000000000000" "0000000aaaa8405a2ec3780c001a006cf07af78c187db104496c58d679f37fcd2d579" "0970cecf9a59fe4a5321b375a039f3faafc71479d283a5b1e66a86b19c4bdc516655d89" - "dbe57d9747747c01dfe"}, + "dbe57d9747747c01dfe", + "0x41b66fcc516bffb9b1db855e1ceed8ac22c4cec0c81778ca358dee49fc46ab9c"}, {"0x24C", "0x5EC1B9F", "0x919A", "0x15752", "0x1FCE1", "0x02f86e0482024c830157528301fce182919a94000000000000000000000000000" "000000000aaaa8405ec1b9f80c080a03e2f59ac9ca852034c2c1da35a742ca19fdd91" "0aa5d2ed49ab8ad27a2fcb2b10a03ac1c29c26723c58f91400fb6dfb5f5b837467b1c37" - "7541b47dae474dddbe469"}, + "7541b47dae474dddbe469", + "0xdb703381b81f88b43b5f35a202d9d408a19211fa9f5d90eabb51203a6f445ea9"}, {"0x384", "0x1CFE6D1", "0x12915", "0x220A", "0x1B841", "0x02f86e0482038482220a8301b8418301291594000000000000000000000000000" "000000000aaaa8401cfe6d180c001a0f7ffc5bca2512860f8236360159bf303dcfab715" "46b6a0032df0306f3739d0c4a05d38fe2c4edebdc1edc157034f780c53a0e5ae089e572" - "20745bd48bcb10cdf87"}, + "20745bd48bcb10cdf87", + "0xdae486b05621c1f58b5fed7aebed646dccb8ba24da3627ec30c8c28b75ba3337"}, {"0x2C5", "0x62D8DB", "0x6EAF", "0x150EC", "0x171AC", "0x02f86d048202c5830150ec830171ac826eaf94000000000000000000000000000" "000000000aaaa8362d8db80c001a0a61a5710512f346c9996377f7b564ccb64c73a5fdb" "615499adb1250498f3e01aa002d10429572cecfaa911a58bbe05f2b26e4c3aee3402202" - "153a93692849add11"}, + "153a93692849add11", + "0xa9361a1b88883848872a9140da45e4ef7a029d3699c5703e164414a00536029c"}, {"0x3AB", "0x2A76B9", "0xAFF7", "0xB0A0", "0x16600", "0x02f86c048203ab82b0a08301660082aff79400000000000000000000000000000" "0000000aaaa832a76b980c001a0191f0f6667a20cefc0b454e344cc01108aafbdc4e4e5" "ed88fdd1b5d108495b31a020879042b0f8d3807609f18fe42a9820de53c8a0ea1d0a2d5" - "0f8f5e92a94f00d"}, + "0f8f5e92a94f00d", + "0xdff2cae411536ce697ad490c5166ea378b9d8d64c5c63c167b4e2cf2319705d0"}, {"0x77", "0x3E6C7F3", "0xF385", "0x6091", "0x1A4D1", "0x02f86b04778260918301a4d182f38594000000000000000000000000000000000" "000aaaa8403e6c7f380c001a05e40977f4064a2bc08785e422ed8a47b56aa219abe9325" "1d2b3b4d0cf937b8c0a071e600cd15589c3607bd9627314b99e9b5976bd427b774aa685" - "bd0d036b1771e"}}; + "bd0d036b1771e", + "0x863c02549182b91f1764714b93d7e882f010539c0907adaf4de761f7b06a713c"}}; for (const auto& entry : cases) { + SCOPED_TRACE(entry.signed_tx); std::vector private_key; EXPECT_TRUE(base::HexStringToBytes( "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", @@ -125,7 +137,8 @@ TEST(Eip1559TransactionUnitTest, GetSignedTransaction) { *Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New(entry.nonce, "0x00", entry.gas_limit, "0x000000000000000000000000000000000000aaaa", - entry.value, std::vector()), + entry.value, std::vector(), false, + absl::nullopt), "0x04", entry.max_priority_fee_per_gas, entry.max_fee_per_gas, nullptr)); @@ -134,6 +147,7 @@ TEST(Eip1559TransactionUnitTest, GetSignedTransaction) { key.Sign(tx.GetMessageToSign(), &recid); tx.ProcessSignature(signature, recid); EXPECT_EQ(tx.GetSignedTransaction(), entry.signed_tx); + EXPECT_EQ(tx.GetTransactionHash(), entry.hash); } } @@ -142,7 +156,8 @@ TEST(Eip1559TransactionUnitTest, GetUpfrontCost) { *Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x00", "0x00", "0x64", "0x0101010101010101010101010101010101010101", - "0x06", std::vector()), + "0x06", std::vector(), false, + absl::nullopt), "0x04", "0x8", "0xA", nullptr)); EXPECT_EQ(tx.GetUpfrontCost(), uint256_t(806)); EXPECT_EQ(tx.GetUpfrontCost(0), uint256_t(806)); @@ -154,7 +169,8 @@ TEST(Eip1559TransactionUnitTest, Serialization) { *Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x09", "0x4a817c800", "0x5208", "0x3535353535353535353535353535353535353535", - "0xde0b6b3a7640000", std::vector()), + "0xde0b6b3a7640000", std::vector(), false, + absl::nullopt), "0x15BE", "0x7B", "0x1C8", GetMojomGasEstimation())); auto* access_list = tx.access_list(); @@ -176,7 +192,7 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { auto tx = Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x01", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "0x15BE", "0x7B", "0x1C8", GetMojomGasEstimation())); ASSERT_TRUE(tx); EXPECT_EQ(tx->nonce().value(), uint256_t(1)); @@ -206,7 +222,7 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { tx = Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "0x15BE", "0x7B", "0x1C8", nullptr)); ASSERT_TRUE(tx); EXPECT_FALSE(tx->nonce()); @@ -215,7 +231,7 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { EXPECT_FALSE(Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("123", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "0x15BE", "0x7B", "0x1C8", nullptr))); // Make sure chain id, and the max priority fee fields must all have @@ -223,17 +239,17 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { EXPECT_FALSE(Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x1", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "", "0x7B", "0x1C8", nullptr))); EXPECT_FALSE(Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x1", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "0x15BE", "", "0x1C8", nullptr))); EXPECT_FALSE(Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x1", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "0x15BE", "0x7B", "", nullptr))); // But missing data is allowed when strict is false @@ -241,7 +257,7 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { mojom::TxData1559::New( mojom::TxData::New("", "0x3E8", "", "0x3535353535353535353535353535353535353535", "", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "", "0x7B", "0x1C8", nullptr), false); ASSERT_TRUE(tx); @@ -265,7 +281,7 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { mojom::TxData1559::New( mojom::TxData::New("", "0x3E8", "", "0x3535353535353535353535353535353535353535", "", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "0x15BE", "", "", nullptr), false); ASSERT_TRUE(tx); @@ -288,7 +304,7 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { tx = Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x01", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), "0x15BE", "0x7B", "0x1C8", missing_fields_gas_estimation.Clone())); EXPECT_EQ(tx->gas_estimation(), Eip1559Transaction::GasEstimation()); @@ -296,7 +312,8 @@ TEST(Eip1559TransactionUnitTest, FromTxData) { mojom::TxData1559::New( mojom::TxData::New("0x01", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", - "0x2A", std::vector{1}), + "0x2A", std::vector{1}, false, + absl::nullopt), "0x15BE", "0x7B", "0x1C8", missing_fields_gas_estimation.Clone()), false); EXPECT_EQ(tx->gas_estimation(), Eip1559Transaction::GasEstimation()); diff --git a/components/brave_wallet/browser/eip2930_transaction.cc b/components/brave_wallet/browser/eip2930_transaction.cc index edd16d9e9646..4077ba5a5072 100644 --- a/components/brave_wallet/browser/eip2930_transaction.cc +++ b/components/brave_wallet/browser/eip2930_transaction.cc @@ -176,26 +176,14 @@ std::string Eip2930Transaction::GetSignedTransaction() const { DCHECK(IsSigned()); DCHECK(nonce_); - base::Value::List list; - list.Append(RLPUint256ToBlob(chain_id_)); - list.Append(RLPUint256ToBlob(nonce_.value())); - list.Append(RLPUint256ToBlob(gas_price_)); - list.Append(RLPUint256ToBlob(gas_limit_)); - list.Append(base::Value::BlobStorage(to_.bytes())); - list.Append(RLPUint256ToBlob(value_)); - list.Append(base::Value(data_)); - list.Append(base::Value(AccessListToValue(access_list_))); - list.Append(RLPUint256ToBlob(v_)); - list.Append(base::Value(r_)); - list.Append(base::Value(s_)); - - std::vector result; - result.push_back(type_); + return ToHex(Serialize()); +} - const std::string rlp_msg = RLPEncode(base::Value(std::move(list))); - result.insert(result.end(), rlp_msg.begin(), rlp_msg.end()); +std::string Eip2930Transaction::GetTransactionHash() const { + DCHECK(IsSigned()); + DCHECK(nonce_); - return ToHex(result); + return ToHex(KeccakHash(Serialize())); } void Eip2930Transaction::ProcessSignature(const std::vector signature, @@ -227,4 +215,27 @@ uint256_t Eip2930Transaction::GetDataFee() const { return fee; } +std::vector Eip2930Transaction::Serialize() const { + base::Value::List list; + list.Append(RLPUint256ToBlob(chain_id_)); + list.Append(RLPUint256ToBlob(nonce_.value())); + list.Append(RLPUint256ToBlob(gas_price_)); + list.Append(RLPUint256ToBlob(gas_limit_)); + list.Append(base::Value::BlobStorage(to_.bytes())); + list.Append(RLPUint256ToBlob(value_)); + list.Append(base::Value(data_)); + list.Append(base::Value(AccessListToValue(access_list_))); + list.Append(RLPUint256ToBlob(v_)); + list.Append(base::Value(r_)); + list.Append(base::Value(s_)); + + std::vector result; + result.push_back(type_); + + const std::string rlp_msg = RLPEncode(base::Value(std::move(list))); + result.insert(result.end(), rlp_msg.begin(), rlp_msg.end()); + + return result; +} + } // namespace brave_wallet diff --git a/components/brave_wallet/browser/eip2930_transaction.h b/components/brave_wallet/browser/eip2930_transaction.h index d257ee4b12ed..28690b196634 100644 --- a/components/brave_wallet/browser/eip2930_transaction.h +++ b/components/brave_wallet/browser/eip2930_transaction.h @@ -59,6 +59,10 @@ class Eip2930Transaction : public EthTransaction { // accessList, signatureYParity, signatureR, signatureS]) std::string GetSignedTransaction() const override; + // keccack(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, + // accessList, signatureYParity, signatureR, signatureS])) + std::string GetTransactionHash() const override; + void ProcessSignature(const std::vector signature, int recid, uint256_t chain_id = 0) override; @@ -80,6 +84,9 @@ class Eip2930Transaction : public EthTransaction { uint256_t chain_id_; AccessList access_list_; + + private: + std::vector Serialize() const; }; } // namespace brave_wallet diff --git a/components/brave_wallet/browser/eip2930_transaction_unittest.cc b/components/brave_wallet/browser/eip2930_transaction_unittest.cc index dfdf22989470..f27a84751d0e 100644 --- a/components/brave_wallet/browser/eip2930_transaction_unittest.cc +++ b/components/brave_wallet/browser/eip2930_transaction_unittest.cc @@ -78,7 +78,7 @@ TEST(Eip2930TransactionUnitTest, GetMessageToSign) { Eip2930Transaction tx = *Eip2930Transaction::FromTxData( mojom::TxData::New("0x00", "0x00", "0x00", "0x0101010101010101010101010101010101010101", "0x00", - data), + data, false, absl::nullopt), 0x01); ASSERT_EQ(tx.type(), 1); auto* access_list = tx.access_list(); @@ -95,11 +95,11 @@ TEST(Eip2930TransactionUnitTest, GetMessageToSign) { "78528e2724aa359c58c13e43a7c467eb721ce8d410c2a12ee62943a3aaefb60b"); } -TEST(Eip2930TransactionUnitTest, GetSignedTransaction) { +TEST(Eip2930TransactionUnitTest, GetSignedTransactionAndHash) { Eip2930Transaction tx = *Eip2930Transaction::FromTxData( mojom::TxData::New("0x00", "0x3b9aca00", "0x62d4", "0xdf0a88b2b68c673713a8ec826003676f272e3573", "0x01", - std::vector()), + std::vector(), false, absl::nullopt), 0x796f6c6f763378); ASSERT_EQ(tx.type(), 1); auto* access_list = tx.access_list(); @@ -139,6 +139,9 @@ TEST(Eip2930TransactionUnitTest, GetSignedTransaction) { "00000000000000000000000000000000000000000000000000000000000080a0294ac940" "77b35057971e6b4b06dfdf55a6fbed819133a6c1d31e187f1bca938da00be950468ba1c2" "5a5cb50e9f6d8aa13c8cd21f24ba909402775b262ac76d374d"); + EXPECT_EQ( + tx.GetTransactionHash(), + "0xbbd570a3c6acc9bb7da0d5c0322fe4ea2a300db80226f7df4fef39b2d6649eec"); EXPECT_EQ(tx.v_, (uint256_t)0); EXPECT_EQ(base::ToLowerASCII(base::HexEncode(tx.r_)), @@ -151,7 +154,8 @@ TEST(Eip2930TransactionUnitTest, Serialization) { Eip2930Transaction tx = *Eip2930Transaction::FromTxData( mojom::TxData::New("0x09", "0x4a817c800", "0x5208", "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()), + "0x0de0b6b3a7640000", std::vector(), false, + absl::nullopt), 0x15BE); auto* access_list = tx.access_list(); Eip2930Transaction::AccessListItem item_a; @@ -173,7 +177,7 @@ TEST(Eip2930TransactionUnitTest, GetBaseFee) { Eip2930Transaction tx = *Eip2930Transaction::FromTxData( mojom::TxData::New("0x0", "0x0", "0x0", "0x3535353535353535353535353535353535353535", "0x0", - data), + data, false, absl::nullopt), 0x15BE); auto* access_list = tx.access_list(); @@ -190,7 +194,9 @@ TEST(Eip2930TransactionUnitTest, GetBaseFee) { EXPECT_EQ(tx.GetBaseFee(), fee); Eip2930Transaction tx2 = *Eip2930Transaction::FromTxData( - mojom::TxData::New("0x0", "0x0", "0x0", "", "0x0", data), 0x15BE); + mojom::TxData::New("0x0", "0x0", "0x0", "", "0x0", data, false, + absl::nullopt), + 0x15BE); *tx2.access_list() = *tx.access_list(); // Plus contract creation const uint256_t fee2 = fee + uint256_t(32000); @@ -200,7 +206,7 @@ TEST(Eip2930TransactionUnitTest, GetBaseFee) { Eip2930Transaction tx3 = *Eip2930Transaction::FromTxData( mojom::TxData::New("0x0", "0x0", "0x0", "0x3535353535353535353535353535353535353535", "0x0", - std::vector()), + std::vector(), false, absl::nullopt), 0x15BE); auto* access_list3 = tx3.access_list(); @@ -217,7 +223,7 @@ TEST(Eip2930TransactionUnitTest, FromTxData) { auto tx = Eip2930Transaction::FromTxData( mojom::TxData::New("0x01", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), 1); ASSERT_TRUE(tx); EXPECT_EQ(tx->nonce(), uint256_t(1)); @@ -233,7 +239,7 @@ TEST(Eip2930TransactionUnitTest, FromTxData) { tx = Eip2930Transaction::FromTxData( mojom::TxData::New("", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), 0); ASSERT_TRUE(tx); EXPECT_FALSE(tx->nonce()); @@ -243,14 +249,14 @@ TEST(Eip2930TransactionUnitTest, FromTxData) { EXPECT_FALSE(Eip2930Transaction::FromTxData( mojom::TxData::New("0x1", "0x3E8", "", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), 0)); // But missing data is allowed when strict is false tx = Eip2930Transaction::FromTxData( mojom::TxData::New("", "0x3E8", "", "0x3535353535353535353535353535353535353535", "", - std::vector{1}), + std::vector{1}, false, absl::nullopt), 1, false); ASSERT_TRUE(tx); // Empty nonce should be nullopt diff --git a/components/brave_wallet/browser/eth_pending_tx_tracker.cc b/components/brave_wallet/browser/eth_pending_tx_tracker.cc index e92986a71759..b071df2c7fc0 100644 --- a/components/brave_wallet/browser/eth_pending_tx_tracker.cc +++ b/components/brave_wallet/browser/eth_pending_tx_tracker.cc @@ -35,6 +35,12 @@ bool EthPendingTxTracker::UpdatePendingTransactions(size_t* num_pending) { auto pending_transactions = tx_state_manager_->GetTransactionsByStatus( mojom::TransactionStatus::Submitted, absl::nullopt); + auto signed_transactions = tx_state_manager_->GetTransactionsByStatus( + mojom::TransactionStatus::Signed, absl::nullopt); + pending_transactions.insert( + pending_transactions.end(), + std::make_move_iterator(signed_transactions.begin()), + std::make_move_iterator(signed_transactions.end())); for (const auto& pending_transaction : pending_transactions) { if (IsNonceTaken(static_cast(*pending_transaction))) { DropTransaction(pending_transaction.get()); diff --git a/components/brave_wallet/browser/eth_transaction.cc b/components/brave_wallet/browser/eth_transaction.cc index 81af03e22711..26a27731af43 100644 --- a/components/brave_wallet/browser/eth_transaction.cc +++ b/components/brave_wallet/browser/eth_transaction.cc @@ -173,18 +173,15 @@ std::vector EthTransaction::GetMessageToSign(uint256_t chain_id, std::string EthTransaction::GetSignedTransaction() const { DCHECK(nonce_); - base::Value::List list; - list.Append(RLPUint256ToBlob(nonce_.value())); - list.Append(RLPUint256ToBlob(gas_price_)); - list.Append(RLPUint256ToBlob(gas_limit_)); - list.Append(base::Value::BlobStorage(to_.bytes())); - list.Append(RLPUint256ToBlob(value_)); - list.Append(base::Value(data_)); - list.Append(RLPUint256ToBlob(v_)); - list.Append(base::Value(r_)); - list.Append(base::Value(s_)); - return ToHex(RLPEncode(base::Value(std::move(list)))); + return ToHex(RLPEncode(Serialize())); +} + +std::string EthTransaction::GetTransactionHash() const { + DCHECK(IsSigned()); + DCHECK(nonce_); + + return KeccakHash(RLPEncode(Serialize())); } bool EthTransaction::ProcessVRS(const std::string& v, @@ -278,4 +275,19 @@ uint256_t EthTransaction::GetUpfrontCost(uint256_t block_base_fee) const { return gas_limit_ * gas_price_ + value_; } +base::Value EthTransaction::Serialize() const { + base::Value::List list; + list.Append(RLPUint256ToBlob(nonce_.value())); + list.Append(RLPUint256ToBlob(gas_price_)); + list.Append(RLPUint256ToBlob(gas_limit_)); + list.Append(base::Value::BlobStorage(to_.bytes())); + list.Append(RLPUint256ToBlob(value_)); + list.Append(base::Value(data_)); + list.Append(RLPUint256ToBlob(v_)); + list.Append(base::Value(r_)); + list.Append(base::Value(s_)); + + return base::Value(std::move(list)); +} + } // namespace brave_wallet diff --git a/components/brave_wallet/browser/eth_transaction.h b/components/brave_wallet/browser/eth_transaction.h index 13df870d6525..bd34cac65dea 100644 --- a/components/brave_wallet/browser/eth_transaction.h +++ b/components/brave_wallet/browser/eth_transaction.h @@ -72,6 +72,9 @@ class EthTransaction { // return rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]) virtual std::string GetSignedTransaction() const; + // return keccack(rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])) + virtual std::string GetTransactionHash() const; + // signature and recid will be used to produce v, r, s // Support EIP-155 chain id virtual void ProcessSignature(const std::vector signature, @@ -114,9 +117,12 @@ class EthTransaction { const std::vector& data); private: - FRIEND_TEST_ALL_PREFIXES(EthTransactionUnitTest, GetSignedTransaction); + FRIEND_TEST_ALL_PREFIXES(EthTransactionUnitTest, GetSignedTransactionAndHash); FRIEND_TEST_ALL_PREFIXES(EthTransactionUnitTest, TransactionAndValue); - FRIEND_TEST_ALL_PREFIXES(Eip2930TransactionUnitTest, GetSignedTransaction); + FRIEND_TEST_ALL_PREFIXES(Eip2930TransactionUnitTest, + GetSignedTransactionAndHash); + + base::Value Serialize() const; }; } // namespace brave_wallet diff --git a/components/brave_wallet/browser/eth_transaction_unittest.cc b/components/brave_wallet/browser/eth_transaction_unittest.cc index 11bf22a0f82e..6158f8ae0a3c 100644 --- a/components/brave_wallet/browser/eth_transaction_unittest.cc +++ b/components/brave_wallet/browser/eth_transaction_unittest.cc @@ -35,7 +35,7 @@ TEST(EthTransactionUnitTest, GetMessageToSign) { EthTransaction tx1 = *EthTransaction::FromTxData( mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data)); + "0x016345785d8a0000", data, false, absl::nullopt)); EXPECT_EQ(base::ToLowerASCII(base::HexEncode(tx1.GetMessageToSign(0))), "61e1ec33764304dddb55348e7883d4437426f44ab3ef65e6da1e025734c03ff0"); @@ -44,9 +44,10 @@ TEST(EthTransactionUnitTest, GetMessageToSign) { "9ad82175b6921c5525fc52ebc08b97118cc9709952a16b2249a3f42d44614721"); data.clear(); - EthTransaction tx2 = *EthTransaction::FromTxData(mojom::TxData::New( - "0x0b", "0x051f4d5c00", "0x5208", - "0x656e929d6fc0cac52d3d9526d288fe02dcd56fbd", "0x2386f26fc10000", data)); + EthTransaction tx2 = *EthTransaction::FromTxData( + mojom::TxData::New("0x0b", "0x051f4d5c00", "0x5208", + "0x656e929d6fc0cac52d3d9526d288fe02dcd56fbd", + "0x2386f26fc10000", data, false, absl::nullopt)); // with chain id (mainnet) EXPECT_EQ(base::ToLowerASCII(base::HexEncode(tx2.GetMessageToSign(1))), @@ -99,16 +100,16 @@ TEST(EthTransactionUnitTest, GetMessageToSign) { }; for (const auto& entry : cases) { - EthTransaction tx = *EthTransaction::FromTxData( - mojom::TxData::New(entry.nonce, entry.gas_price, entry.gas_limit, - entry.to, entry.value, std::vector())); + EthTransaction tx = *EthTransaction::FromTxData(mojom::TxData::New( + entry.nonce, entry.gas_price, entry.gas_limit, entry.to, entry.value, + std::vector(), false, absl::nullopt)); // with chain id (mainnet) EXPECT_EQ(base::ToLowerASCII(base::HexEncode(tx.GetMessageToSign(1))), entry.hash); } } -TEST(EthTransactionUnitTest, GetSignedTransaction) { +TEST(EthTransactionUnitTest, GetSignedTransactionAndHash) { std::vector private_key; EXPECT_TRUE(base::HexStringToBytes( "4646464646464646464646464646464646464646464646464646464646464646", @@ -119,10 +120,10 @@ TEST(EthTransactionUnitTest, GetSignedTransaction) { std::unique_ptr, SecureZeroVectorDeleter>( new std::vector(private_key), SecureZeroVectorDeleter())); - EthTransaction tx = *EthTransaction::FromTxData( - mojom::TxData::New("0x09", "0x4a817c800", "0x5208", - "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector())); + EthTransaction tx = *EthTransaction::FromTxData(mojom::TxData::New( + "0x09", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt)); const std::vector message = tx.GetMessageToSign(1); EXPECT_EQ(base::ToLowerASCII(base::HexEncode(message)), @@ -150,6 +151,9 @@ TEST(EthTransactionUnitTest, GetSignedTransaction) { "35880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d" "3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3" "dc64214b297fb1966a3b6d83"); + EXPECT_EQ( + tx.GetTransactionHash(), + "0x33469b22e9f636356c4160a87eb19df52b7412e8eac32a4a55ffe88ea8350788"); EXPECT_TRUE(tx.IsSigned()); EXPECT_EQ(tx.v_, (uint256_t)37); @@ -172,6 +176,9 @@ TEST(EthTransactionUnitTest, GetSignedTransaction) { "35880de0b6b3a764000080820a96a011d1f0b9de554ad9e690bb8355507007731b" "741e232ecb0dc183154c10c77875a03a4b32607c8c2287e82ae8c2a334d8412baf" "15e52ee25c531762dc34252a1365"); + EXPECT_EQ( + tx.GetTransactionHash(), + "0x3874c51841f3290a1b3e23152c474d361cc34e5b58f4adfcf4ff04bd77ed6b7a"); EXPECT_TRUE(tx.IsSigned()); EXPECT_EQ(tx.v_, (uint256_t)2710); EXPECT_EQ(base::HexEncode(tx.r_), @@ -181,10 +188,10 @@ TEST(EthTransactionUnitTest, GetSignedTransaction) { } TEST(EthTransactionUnitTest, TransactionAndValue) { - EthTransaction tx = *EthTransaction::FromTxData( - mojom::TxData::New("0x09", "0x4a817c800", "0x5208", - "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector())); + EthTransaction tx = *EthTransaction::FromTxData(mojom::TxData::New( + "0x09", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt)); base::Value::Dict tx_value = tx.ToValue(); auto tx_from_value = EthTransaction::FromValue(tx_value); ASSERT_NE(tx_from_value, absl::nullopt); @@ -217,21 +224,21 @@ TEST(EthTransactionUnitTest, GetDataFee) { EthTransaction tx2 = *EthTransaction::FromTxData( mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data)); + "0x016345785d8a0000", data, false, absl::nullopt)); EXPECT_EQ(tx2.GetDataFee(), uint256_t(1716)); } TEST(EthTransactionUnitTest, GetUpFrontCost) { EthTransaction tx = *EthTransaction::FromTxData(mojom::TxData::New( "0x00", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", - "0x2A", std::vector())); + "0x2A", std::vector(), false, absl::nullopt)); EXPECT_EQ(tx.GetUpfrontCost(), uint256_t(10000000042)); } TEST(EthTransactionUnitTest, FromTxData) { auto tx = EthTransaction::FromTxData(mojom::TxData::New( "0x01", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", - "0x2A", std::vector{1})); + "0x2A", std::vector{1}, false, absl::nullopt)); ASSERT_TRUE(tx); EXPECT_EQ(tx->nonce(), uint256_t(1)); EXPECT_EQ(tx->gas_price(), uint256_t(1000)); @@ -244,26 +251,26 @@ TEST(EthTransactionUnitTest, FromTxData) { // Empty nonce tx = EthTransaction::FromTxData(mojom::TxData::New( "", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", - "0x2A", std::vector{1})); + "0x2A", std::vector{1}, false, absl::nullopt)); ASSERT_TRUE(tx); EXPECT_FALSE(tx->nonce()); // Missing values should not parse correctly EXPECT_FALSE(EthTransaction::FromTxData(mojom::TxData::New( "0x01", "", "0x989680", "0x3535353535353535353535353535353535353535", - "0x2A", std::vector{1}))); + "0x2A", std::vector{1}, false, absl::nullopt))); EXPECT_FALSE(EthTransaction::FromTxData(mojom::TxData::New( "0x01", "0x3E8", "", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}))); + std::vector{1}, false, absl::nullopt))); EXPECT_FALSE(EthTransaction::FromTxData(mojom::TxData::New( "0x01", "0x3E8", "0x989680", "0x3535353535353535353535353535353535353535", - "", std::vector{1}))); + "", std::vector{1}, false, absl::nullopt))); // But missing data is allowed when strict is false tx = EthTransaction::FromTxData( mojom::TxData::New("", "0x3E8", "", "0x3535353535353535353535353535353535353535", "", - std::vector{1}), + std::vector{1}, false, absl::nullopt), false); ASSERT_TRUE(tx); // Empty nonce should be absl::nullopt @@ -278,7 +285,7 @@ TEST(EthTransactionUnitTest, FromTxData) { tx = EthTransaction::FromTxData( mojom::TxData::New("0x1", "", "0x989680", "0x3535353535353535353535353535353535353535", "0x2A", - std::vector{1}), + std::vector{1}, false, absl::nullopt), false); ASSERT_TRUE(tx); // Unspecified value defaults to 0 diff --git a/components/brave_wallet/browser/eth_tx_manager.cc b/components/brave_wallet/browser/eth_tx_manager.cc index 3dd8d6057018..152452644c88 100644 --- a/components/brave_wallet/browser/eth_tx_manager.cc +++ b/components/brave_wallet/browser/eth_tx_manager.cc @@ -188,18 +188,19 @@ void EthTxManager::AddUnapprovedTransaction( json_rpc_service_->GetGasPrice(base::BindOnce( &EthTxManager::OnGetGasPrice, weak_factory_.GetWeakPtr(), from, origin, tx_data->to, tx_data->value, data, gas_limit, group_id, - std::move(tx_ptr), std::move(callback))); + std::move(tx_ptr), std::move(callback), tx_data->sign_only)); } else if (!tx_ptr->gas_limit()) { json_rpc_service_->GetEstimateGas( from, tx_data->to, "" /* gas */, "" /* gas_price */, tx_data->value, data, base::BindOnce(&EthTxManager::ContinueAddUnapprovedTransaction, weak_factory_.GetWeakPtr(), from, origin, group_id, - std::move(tx_ptr), std::move(callback))); + std::move(tx_ptr), std::move(callback), + tx_data->sign_only)); } else { - ContinueAddUnapprovedTransaction(from, origin, group_id, std::move(tx_ptr), - std::move(callback), gas_limit, - mojom::ProviderError::kSuccess, ""); + ContinueAddUnapprovedTransaction( + from, origin, group_id, std::move(tx_ptr), std::move(callback), + tx_data->sign_only, gas_limit, mojom::ProviderError::kSuccess, ""); } } @@ -212,6 +213,7 @@ void EthTxManager::OnGetGasPrice(const std::string& from, const absl::optional& group_id, std::unique_ptr tx, AddUnapprovedTransactionCallback callback, + bool sign_only, const std::string& result, mojom::ProviderError error, const std::string& error_message) { @@ -231,10 +233,10 @@ void EthTxManager::OnGetGasPrice(const std::string& from, from, to, "" /* gas */, "" /* gas_price */, value, data, base::BindOnce(&EthTxManager::ContinueAddUnapprovedTransaction, weak_factory_.GetWeakPtr(), from, origin, group_id, - std::move(tx), std::move(callback))); + std::move(tx), std::move(callback), sign_only)); } else { ContinueAddUnapprovedTransaction(from, origin, group_id, std::move(tx), - std::move(callback), gas_limit, + std::move(callback), sign_only, gas_limit, mojom::ProviderError::kSuccess, ""); } } @@ -245,6 +247,7 @@ void EthTxManager::ContinueAddUnapprovedTransaction( const absl::optional& group_id, std::unique_ptr tx, AddUnapprovedTransactionCallback callback, + bool sign_only, const std::string& result, mojom::ProviderError error, const std::string& error_message) { @@ -280,6 +283,7 @@ void EthTxManager::ContinueAddUnapprovedTransaction( meta.set_group_id(group_id); meta.set_created_time(base::Time::Now()); meta.set_status(mojom::TransactionStatus::Unapproved); + meta.set_sign_only(sign_only); tx_state_manager_->AddOrUpdateTx(meta); std::move(callback).Run(true, meta.id(), ""); } @@ -316,23 +320,24 @@ void EthTxManager::AddUnapproved1559Transaction( // as required by geth. This is typically the case with ETHSend. const std::string data = tx_data->base_data->data.empty() ? "" : ToHex(tx_data->base_data->data); + bool sign_only = tx_data->base_data->sign_only; if (!tx_ptr->max_priority_fee_per_gas() || !tx_ptr->max_fee_per_gas()) { GetGasEstimation1559(base::BindOnce( &EthTxManager::OnGetGasOracleForUnapprovedTransaction, weak_factory_.GetWeakPtr(), from, origin, tx_data->base_data->to, tx_data->base_data->value, data, gas_limit, group_id, std::move(tx_ptr), - std::move(callback))); + std::move(callback), sign_only)); } else if (gas_limit.empty()) { json_rpc_service_->GetEstimateGas( from, tx_data->base_data->to, "" /* gas */, "" /* gas_price */, tx_data->base_data->value, data, base::BindOnce(&EthTxManager::ContinueAddUnapprovedTransaction, weak_factory_.GetWeakPtr(), from, origin, group_id, - std::move(tx_ptr), std::move(callback))); + std::move(tx_ptr), std::move(callback), sign_only)); } else { ContinueAddUnapprovedTransaction(from, origin, group_id, std::move(tx_ptr), - std::move(callback), gas_limit, + std::move(callback), sign_only, gas_limit, mojom::ProviderError::kSuccess, ""); } } @@ -347,6 +352,7 @@ void EthTxManager::OnGetGasOracleForUnapprovedTransaction( const absl::optional& group_id, std::unique_ptr tx, AddUnapprovedTransactionCallback callback, + bool sign_only, mojom::GasEstimation1559Ptr gas_estimation) { auto estimation = Eip1559Transaction::GasEstimation::FromMojomGasEstimation1559( @@ -367,10 +373,10 @@ void EthTxManager::OnGetGasOracleForUnapprovedTransaction( from, to, "" /* gas */, "" /* gas_price */, value, data, base::BindOnce(&EthTxManager::ContinueAddUnapprovedTransaction, weak_factory_.GetWeakPtr(), from, origin, group_id, - std::move(tx), std::move(callback))); + std::move(tx), std::move(callback), sign_only)); } else { ContinueAddUnapprovedTransaction(from, origin, group_id, std::move(tx), - std::move(callback), gas_limit, + std::move(callback), sign_only, gas_limit, mojom::ProviderError::kSuccess, ""); } } @@ -554,8 +560,20 @@ void EthTxManager::OnGetNextNonce(std::unique_ptr meta, l10n_util::GetStringUTF8(IDS_WALLET_SIGN_TRANSACTION_ERROR)); return; } - PublishTransaction(meta->id(), meta->tx()->GetSignedTransaction(), - std::move(callback)); + if (meta->sign_only()) { + meta->set_status(mojom::TransactionStatus::Signed); + meta->set_tx_hash(meta->tx()->GetTransactionHash()); + tx_state_manager_->AddOrUpdateTx(*meta); + + std::move(callback).Run(true, + mojom::ProviderErrorUnion::NewProviderError( + mojom::ProviderError::kSuccess), + ""); + UpdatePendingTransactions(); + } else { + PublishTransaction(meta->id(), meta->tx()->GetSignedTransaction(), + std::move(callback)); + } } void EthTxManager::PublishTransaction(const std::string& tx_meta_id, @@ -986,7 +1004,7 @@ void EthTxManager::ContinueSpeedupOrCancelTransaction( tx->set_gas_price(std::max(latest_estimate_gas_price, increased_gas_price)); ContinueAddUnapprovedTransaction(from, origin, group_id, std::move(tx), - std::move(callback), gas_limit, + std::move(callback), false, gas_limit, mojom::ProviderError::kSuccess, ""); } @@ -1033,7 +1051,7 @@ void EthTxManager::ContinueSpeedupOrCancel1559Transaction( increased_max_priority_fee_per_gas)); ContinueAddUnapprovedTransaction(from, origin, group_id, std::move(tx), - std::move(callback), gas_limit, + std::move(callback), false, gas_limit, mojom::ProviderError::kSuccess, ""); } @@ -1058,7 +1076,7 @@ void EthTxManager::RetryTransaction(const std::string& tx_meta_id, ContinueAddUnapprovedTransaction( meta->from(), meta->origin(), meta->group_id(), std::move(tx), - std::move(callback), Uint256ValueToHex(meta->tx()->gas_limit()), + std::move(callback), false, Uint256ValueToHex(meta->tx()->gas_limit()), mojom::ProviderError::kSuccess, ""); } diff --git a/components/brave_wallet/browser/eth_tx_manager.h b/components/brave_wallet/browser/eth_tx_manager.h index 8241c8c2c5bd..3f3354cd0e4a 100644 --- a/components/brave_wallet/browser/eth_tx_manager.h +++ b/components/brave_wallet/browser/eth_tx_manager.h @@ -191,6 +191,7 @@ class EthTxManager : public TxManager, public EthBlockTracker::Observer { const absl::optional& group_id, std::unique_ptr tx, AddUnapprovedTransactionCallback callback, + bool sign_only, const std::string& result, mojom::ProviderError error, const std::string& error_message); @@ -200,6 +201,7 @@ class EthTxManager : public TxManager, public EthBlockTracker::Observer { const absl::optional& group_id, std::unique_ptr tx, AddUnapprovedTransactionCallback callback, + bool sign_only, const std::string& result, mojom::ProviderError error, const std::string& error_message); @@ -221,6 +223,7 @@ class EthTxManager : public TxManager, public EthBlockTracker::Observer { const absl::optional& group_id, std::unique_ptr tx, AddUnapprovedTransactionCallback callback, + bool sign_only, mojom::GasEstimation1559Ptr gas_estimation); void UpdatePendingTransactions() override; diff --git a/components/brave_wallet/browser/eth_tx_manager_unittest.cc b/components/brave_wallet/browser/eth_tx_manager_unittest.cc index 79fed9287486..2d95669d780a 100644 --- a/components/brave_wallet/browser/eth_tx_manager_unittest.cc +++ b/components/brave_wallet/browser/eth_tx_manager_unittest.cc @@ -321,7 +321,7 @@ class EthTxManagerUnitTest : public testing::Test { auto tx_data = mojom::TxData::New(nonce, gas_price, "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data); + "0x016345785d8a0000", data, false, absl::nullopt); auto tx = EthTransaction::FromTxData(tx_data, false); ASSERT_TRUE(tx); @@ -357,7 +357,8 @@ class EthTxManagerUnitTest : public testing::Test { auto tx_data1559 = mojom::TxData1559::New( mojom::TxData::New(nonce, "", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data), + "0x016345785d8a0000", data, false, absl::nullopt), + "0x539", max_priority_fee_per_gas, max_fee_per_gas, nullptr); auto tx1559 = Eip1559Transaction::FromTxData(tx_data1559, false); @@ -481,7 +482,7 @@ TEST_F(EthTxManagerUnitTest, AddUnapprovedTransactionWithGasPriceAndGasLimit) { auto tx_data = mojom::TxData::New("0x06", gas_price, gas_limit, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -507,7 +508,7 @@ TEST_F(EthTxManagerUnitTest, WalletOrigin) { auto tx_data = mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -529,7 +530,7 @@ TEST_F(EthTxManagerUnitTest, SomeSiteOrigin) { auto tx_data = mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -553,7 +554,7 @@ TEST_F(EthTxManagerUnitTest, auto tx_data = mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -584,7 +585,7 @@ TEST_F(EthTxManagerUnitTest, auto tx_data_1559 = mojom::TxData1559::New( mojom::TxData::New("0x1", "", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_), + "0x016345785d8a0000", data_, false, absl::nullopt), "0x04", "0x77359400" /* 2 Gwei */, "0xb2d05e000" /* 48 Gwei */, nullptr); // EIP-1559 transaction with group_id @@ -618,7 +619,7 @@ TEST_F(EthTxManagerUnitTest, AddUnapprovedTransactionWithoutGasLimit) { auto tx_data = mojom::TxData::New("0x06", gas_price, "" /* gas_limit */, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -667,7 +668,8 @@ TEST_F(EthTxManagerUnitTest, AddUnapprovedTransactionWithoutGasLimit) { tx_data = mojom::TxData::New("0x06", gas_price, "" /* gas_limit */, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_decoded); + "0x016345785d8a0000", data_decoded, false, + absl::nullopt); SetErrorInterceptor(); callback_called = false; @@ -691,7 +693,7 @@ TEST_F(EthTxManagerUnitTest, AddUnapprovedTransactionWithoutGasPrice) { auto tx_data = mojom::TxData::New("0x06", "" /* gas_price */, gas_limit, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -728,7 +730,7 @@ TEST_F(EthTxManagerUnitTest, auto tx_data = mojom::TxData::New("0x06", "" /* gas_price */, "" /* gas_limit */, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -761,10 +763,10 @@ TEST_F(EthTxManagerUnitTest, TEST_F(EthTxManagerUnitTest, AddUnapprovedTransactionWithoutGasPriceAndGasLimitForEthSend) { - auto tx_data = - mojom::TxData::New("0x06", "" /* gas_price*/, "" /* gas_limit */, - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()); + auto tx_data = mojom::TxData::New( + "0x06", "" /* gas_price*/, "" /* gas_limit */, + "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", "0x016345785d8a0000", + std::vector(), false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -787,10 +789,10 @@ TEST_F(EthTxManagerUnitTest, } TEST_F(EthTxManagerUnitTest, SetGasPriceAndLimitForUnapprovedTransaction) { - auto tx_data = - mojom::TxData::New("0x06", "" /* gas_price*/, "" /* gas_limit */, - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()); + auto tx_data = mojom::TxData::New( + "0x06", "" /* gas_price*/, "" /* gas_limit */, + "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", "0x016345785d8a0000", + std::vector(), false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -877,10 +879,10 @@ TEST_F(EthTxManagerUnitTest, SetGasPriceAndLimitForUnapprovedTransaction) { TEST_F(EthTxManagerUnitTest, SetDataForUnapprovedTransaction) { std::vector initial_data{0U, 1U}; - auto tx_data = - mojom::TxData::New("0x06", "0x11" /* gas_price*/, "0x22" /* gas_limit */, - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", initial_data); + auto tx_data = mojom::TxData::New( + "0x06", "0x11" /* gas_price*/, "0x22" /* gas_limit */, + "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", "0x016345785d8a0000", + initial_data, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; AddUnapprovedTransaction( @@ -928,10 +930,10 @@ TEST_F(EthTxManagerUnitTest, SetDataForUnapprovedTransaction) { } TEST_F(EthTxManagerUnitTest, SetNonceForUnapprovedTransaction) { - auto tx_data = - mojom::TxData::New("0x06", "0x11" /* gas_price*/, "0x22" /* gas_limit */, - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()); + auto tx_data = mojom::TxData::New( + "0x06", "0x11" /* gas_price*/, "0x22" /* gas_limit */, + "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", "0x016345785d8a0000", + std::vector(), false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; AddUnapprovedTransaction( @@ -1011,45 +1013,51 @@ TEST_F(EthTxManagerUnitTest, ValidateTxData) { EXPECT_TRUE(EthTxManager::ValidateTxData( mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + "0x016345785d8a0000", std::vector(), false, + absl::nullopt), &error_message)); // Make sure if params are specified that they are valid hex strings EXPECT_FALSE(EthTxManager::ValidateTxData( mojom::TxData::New("hello", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + "0x016345785d8a0000", std::vector(), false, + absl::nullopt), &error_message)); EXPECT_FALSE(EthTxManager::ValidateTxData( mojom::TxData::New("0x06", "hello", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + "0x016345785d8a0000", std::vector(), false, + absl::nullopt), &error_message)); EXPECT_FALSE(EthTxManager::ValidateTxData( mojom::TxData::New("0x06", "0x09184e72a000", "hello", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + "0x016345785d8a0000", std::vector(), false, + absl::nullopt), &error_message)); EXPECT_FALSE(EthTxManager::ValidateTxData( mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "hello", - "0x016345785d8a0000", std::vector()), + "0x016345785d8a0000", std::vector(), false, + absl::nullopt), &error_message)); EXPECT_FALSE(EthTxManager::ValidateTxData( mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", "hello", - std::vector()), + std::vector(), false, absl::nullopt), &error_message)); // to must not only be a valid hex string but also an address EXPECT_FALSE(EthTxManager::ValidateTxData( mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe", // Invalid address - "hello", std::vector()), + "hello", std::vector(), false, absl::nullopt), &error_message)); // To can't be missing if Data is missing EXPECT_FALSE(EthTxManager::ValidateTxData( mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "", - "0x016345785d8a0000", std::vector()), + "0x016345785d8a0000", std::vector(), false, + absl::nullopt), &error_message)); } @@ -1057,9 +1065,9 @@ TEST_F(EthTxManagerUnitTest, ValidateTxData1559) { std::string error_message; EXPECT_TRUE(EthTxManager::ValidateTxData1559( mojom::TxData1559::New( - mojom::TxData::New("0x00", "", "0x00", - "0x0101010101010101010101010101010101010101", - "0x00", std::vector()), + mojom::TxData::New( + "0x00", "", "0x00", "0x0101010101010101010101010101010101010101", + "0x00", std::vector(), false, absl::nullopt), "0x04", "0x0", "0x1", nullptr), &error_message)); @@ -1068,7 +1076,9 @@ TEST_F(EthTxManagerUnitTest, ValidateTxData1559) { mojom::TxData1559::New( mojom::TxData::New("0x00", "0x1", "0x00", "0x0101010101010101010101010101010101010101", - "0x00", std::vector()), + "0x00", std::vector(), false, + absl::nullopt), + "0x04", "0x0", "0x1", nullptr), &error_message)); } @@ -1077,7 +1087,7 @@ TEST_F(EthTxManagerUnitTest, ProcessHardwareSignature) { auto tx_data = mojom::TxData::New("0x06", "" /* gas_price */, "" /* gas_limit */, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -1119,7 +1129,7 @@ TEST_F(EthTxManagerUnitTest, ProcessHardwareSignatureFail) { auto tx_data = mojom::TxData::New("0x06", "" /* gas_price */, "" /* gas_limit */, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -1174,7 +1184,7 @@ TEST_F(EthTxManagerUnitTest, GetNonceForHardwareTransaction) { auto tx_data = mojom::TxData::New("", "" /* gas_price */, "" /* gas_limit */, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -1226,7 +1236,7 @@ TEST_F(EthTxManagerUnitTest, GetNonceForHardwareTransaction1559) { auto tx_data = mojom::TxData1559::New( mojom::TxData::New("0x00", "", "0x01", "0x0101010101010101010101010101010101010101", "0x00", - std::vector()), + std::vector(), false, absl::nullopt), "0x04", "0x1", "0x1", nullptr); bool callback_called = false; @@ -1304,7 +1314,7 @@ TEST_F(EthTxManagerUnitTest, AddUnapproved1559TransactionWithGasFeeAndLimit) { auto tx_data = mojom::TxData1559::New( mojom::TxData::New("0x1", "", gas_limit, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_), + "0x016345785d8a0000", data_, false, absl::nullopt), "0x04", "0x77359400" /* 2 Gwei */, "0xb2d05e000" /* 48 Gwei */, nullptr); bool callback_called = false; @@ -1333,7 +1343,7 @@ TEST_F(EthTxManagerUnitTest, AddUnapproved1559TransactionWithoutGasLimit) { auto tx_data = mojom::TxData1559::New( mojom::TxData::New("0x1", "", "", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_), + "0x016345785d8a0000", data_, false, absl::nullopt), "0x04", "0x77359400" /* 2 Gwei */, "0xb2d05e000" /* 48 Gwei */, nullptr); bool callback_called = false; @@ -1363,7 +1373,7 @@ TEST_F(EthTxManagerUnitTest, AddUnapproved1559TransactionWithoutGasFee) { auto tx_data = mojom::TxData1559::New( mojom::TxData::New("0x1", "", gas_limit, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_), + "0x016345785d8a0000", data_, false, absl::nullopt), "0x04", "", "", nullptr); bool callback_called = false; @@ -1395,7 +1405,7 @@ TEST_F(EthTxManagerUnitTest, auto tx_data = mojom::TxData1559::New( mojom::TxData::New("0x1", "", "", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_), + "0x016345785d8a0000", data_, false, absl::nullopt), "0x04", "", "", nullptr); bool callback_called = false; @@ -1425,9 +1435,9 @@ TEST_F(EthTxManagerUnitTest, TEST_F(EthTxManagerUnitTest, AddUnapproved1559TransactionWithoutGasFeeAndLimitForEthSend) { auto tx_data = mojom::TxData1559::New( - mojom::TxData::New("0x1", "", "", - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + mojom::TxData::New( + "0x1", "", "", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", + "0x016345785d8a0000", std::vector(), false, absl::nullopt), "0x04", "", "", nullptr); bool callback_called = false; @@ -1460,9 +1470,9 @@ TEST_F(EthTxManagerUnitTest, const std::string gas_limit = "0x0974"; auto tx_data = mojom::TxData1559::New( - mojom::TxData::New("0x1", "", gas_limit, - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + mojom::TxData::New( + "0x1", "", gas_limit, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", + "0x016345785d8a0000", std::vector(), false, absl::nullopt), "0x04", "0x77359400" /* 2 Gwei */, "0xb2d05e000" /* 48 Gwei */, nullptr); bool callback_called = false; @@ -1490,9 +1500,9 @@ TEST_F(EthTxManagerUnitTest, TEST_F(EthTxManagerUnitTest, AddUnapproved1559TransactionWithoutGasLimitForEthSend) { auto tx_data = mojom::TxData1559::New( - mojom::TxData::New("0x1", "", "", - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + mojom::TxData::New( + "0x1", "", "", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", + "0x016345785d8a0000", std::vector(), false, absl::nullopt), "0x04", "0x77359400" /* 2 Gwei */, "0xb2d05e000" /* 48 Gwei */, nullptr); bool callback_called = false; std::string tx_meta_id; @@ -1520,9 +1530,9 @@ TEST_F(EthTxManagerUnitTest, AddUnapproved1559TransactionWithoutGasFeeForEthSend) { const std::string gas_limit = "0x0974"; auto tx_data = mojom::TxData1559::New( - mojom::TxData::New("0x1", "", gas_limit, - "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()), + mojom::TxData::New( + "0x1", "", gas_limit, "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", + "0x016345785d8a0000", std::vector(), false, absl::nullopt), "0x04", "", "", nullptr); bool callback_called = false; @@ -1555,7 +1565,7 @@ TEST_F(EthTxManagerUnitTest, SetGasFeeAndLimitForUnapprovedTransaction) { auto tx_data = mojom::TxData1559::New( mojom::TxData::New("0x1", "", "", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_), + "0x016345785d8a0000", data_, false, absl::nullopt), "0x04", "", "", nullptr); bool callback_called = false; std::string tx_meta_id; @@ -1673,7 +1683,7 @@ TEST_F(EthTxManagerUnitTest, auto tx_data = mojom::TxData::New("0x06", "0x09184e72a000", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); bool callback_called = false; std::string tx_meta_id; @@ -2017,7 +2027,7 @@ TEST_F(EthTxManagerUnitTest, RetryTransaction) { auto tx_data = mojom::TxData::New("0x07", "0x17fcf18322", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_); + "0x016345785d8a0000", data_, false, absl::nullopt); auto tx = EthTransaction::FromTxData(tx_data, false); ASSERT_TRUE(tx); @@ -2048,7 +2058,7 @@ TEST_F(EthTxManagerUnitTest, RetryTransaction) { auto tx_data1559 = mojom::TxData1559::New( mojom::TxData::New("0x08", "", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", data_), + "0x016345785d8a0000", data_, false, absl::nullopt), "0x539", "0x77359400" /* 2 Gwei */, "0xb2d05e000" /* 48 Gwei */, nullptr); auto tx1559 = Eip1559Transaction::FromTxData(tx_data1559, false); @@ -2197,7 +2207,7 @@ TEST_F(EthTxManagerUnitTest, Reset) { meta.set_status(mojom::TransactionStatus::Unapproved); auto tx_data = mojom::TxData::New( "0x1", "0x1", "0x0974", "0xbe862ad9abfe6f22bcb087716c7d89a26051f74c", - "0x016345785d8a0000", std::vector()); + "0x016345785d8a0000", std::vector(), false, absl::nullopt); auto tx = EthTransaction::FromTxData(tx_data, false); meta.set_tx(std::make_unique(*tx)); eth_tx_manager()->tx_state_manager_->AddOrUpdateTx(meta); diff --git a/components/brave_wallet/browser/eth_tx_meta.cc b/components/brave_wallet/browser/eth_tx_meta.cc index a988650cf6fd..48789d3299d3 100644 --- a/components/brave_wallet/browser/eth_tx_meta.cc +++ b/components/brave_wallet/browser/eth_tx_meta.cc @@ -33,6 +33,7 @@ base::Value::Dict EthTxMeta::ToValue() const { base::Value::Dict dict = TxMeta::ToValue(); dict.Set("tx_receipt", TransactionReceiptToValue(tx_receipt_)); dict.Set("tx", tx_->ToValue()); + dict.Set("sign_only", sign_only_); return dict; } @@ -72,6 +73,9 @@ mojom::TransactionInfoPtr EthTxMeta::ToTransactionInfo() const { } else { std::tie(tx_type, tx_params, tx_args) = *tx_info; } + absl::optional signed_transaction; + if (tx_->IsSigned()) + signed_transaction = tx_->GetSignedTransaction(); return mojom::TransactionInfo::New( id_, from_, tx_hash_, @@ -81,7 +85,7 @@ mojom::TransactionInfoPtr EthTxMeta::ToTransactionInfo() const { Uint256ValueToHex(tx_->gas_price()), Uint256ValueToHex(tx_->gas_limit()), tx_->to().ToChecksumAddress(), Uint256ValueToHex(tx_->value()), - tx_->data()), + tx_->data(), sign_only_, signed_transaction), chain_id, max_priority_fee_per_gas, max_fee_per_gas, std::move(gas_estimation_1559_ptr))), status_, tx_type, tx_params, tx_args, diff --git a/components/brave_wallet/browser/eth_tx_meta.h b/components/brave_wallet/browser/eth_tx_meta.h index eb3e22be6b08..f44de1fa9364 100644 --- a/components/brave_wallet/browser/eth_tx_meta.h +++ b/components/brave_wallet/browser/eth_tx_meta.h @@ -37,13 +37,16 @@ class EthTxMeta : public TxMeta { TransactionReceipt tx_receipt() const { return tx_receipt_; } EthTransaction* tx() const { return tx_.get(); } + bool sign_only() const { return sign_only_; } void set_tx_receipt(const TransactionReceipt& tx_receipt) { tx_receipt_ = tx_receipt; } void set_tx(std::unique_ptr tx) { tx_ = std::move(tx); } + void set_sign_only(bool sign_only) { sign_only_ = sign_only; } private: + bool sign_only_ = false; TransactionReceipt tx_receipt_; std::unique_ptr tx_; }; diff --git a/components/brave_wallet/browser/eth_tx_meta_unittest.cc b/components/brave_wallet/browser/eth_tx_meta_unittest.cc index 11aec3c6d059..4d02b87f0f28 100644 --- a/components/brave_wallet/browser/eth_tx_meta_unittest.cc +++ b/components/brave_wallet/browser/eth_tx_meta_unittest.cc @@ -17,11 +17,11 @@ namespace brave_wallet { TEST(EthTxMetaUnitTest, ToTransactionInfo) { // type 0 - std::unique_ptr tx = - std::make_unique(*EthTransaction::FromTxData( - mojom::TxData::New("0x09", "0x4a817c800", "0x5208", - "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()))); + std::unique_ptr tx = std::make_unique( + *EthTransaction::FromTxData(mojom::TxData::New( + "0x09", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt))); EthTxMeta meta(std::move(tx)); meta.set_from( EthAddress::FromHex("0x2f015c60e0be116b1f0cd534704db9c92118fb6a") @@ -70,7 +70,8 @@ TEST(EthTxMetaUnitTest, ToTransactionInfo) { std::make_unique(*Eip2930Transaction::FromTxData( mojom::TxData::New("0x09", "0x4a817c800", "0x5208", "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()), + "0x0de0b6b3a7640000", std::vector(), + false, absl::nullopt), 0x3)); auto* access_list = tx1->access_list(); Eip2930Transaction::AccessListItem item_a; @@ -116,7 +117,8 @@ TEST(EthTxMetaUnitTest, ToTransactionInfo) { *Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x09", "0x4a817c800", "0x5208", "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()), + "0x0de0b6b3a7640000", std::vector(), + false, absl::nullopt), "0x3", "0x1E", "0x32", mojom::GasEstimation1559::New( "0x3b9aca00" /* Hex of 1 * 1e9 */, diff --git a/components/brave_wallet/browser/eth_tx_state_manager.cc b/components/brave_wallet/browser/eth_tx_state_manager.cc index 4f024d63338e..41de8eb6e0d1 100644 --- a/components/brave_wallet/browser/eth_tx_state_manager.cc +++ b/components/brave_wallet/browser/eth_tx_state_manager.cc @@ -67,6 +67,11 @@ std::unique_ptr EthTxStateManager::ValueToTxMeta( const base::Value::Dict* tx = value.FindDict("tx"); if (!tx) return nullptr; + + absl::optional sign_only = value.FindBool("sign_only"); + if (sign_only) + meta->set_sign_only(*sign_only); + absl::optional type = tx->FindInt("type"); if (!type) return nullptr; diff --git a/components/brave_wallet/browser/eth_tx_state_manager_unittest.cc b/components/brave_wallet/browser/eth_tx_state_manager_unittest.cc index 3cf3773a0e4d..f1cbe746e78e 100644 --- a/components/brave_wallet/browser/eth_tx_state_manager_unittest.cc +++ b/components/brave_wallet/browser/eth_tx_state_manager_unittest.cc @@ -70,11 +70,11 @@ class EthTxStateManagerUnitTest : public testing::Test { TEST_F(EthTxStateManagerUnitTest, TxMetaAndValue) { // type 0 - std::unique_ptr tx = - std::make_unique(*EthTransaction::FromTxData( - mojom::TxData::New("0x09", "0x4a817c800", "0x5208", - "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()))); + std::unique_ptr tx = std::make_unique( + *EthTransaction::FromTxData(mojom::TxData::New( + "0x09", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt))); EthTxMeta meta(std::move(tx)); meta.set_id(TxMeta::GenerateMetaID()); meta.set_status(mojom::TransactionStatus::Submitted); @@ -119,6 +119,8 @@ TEST_F(EthTxStateManagerUnitTest, TxMetaAndValue) { EXPECT_EQ(meta_from_value->origin(), meta.origin()); ASSERT_EQ(meta_from_value->tx()->type(), 0); EXPECT_EQ(*meta_from_value->tx(), *meta.tx()); + // optional sign_only will be false by default + EXPECT_FALSE(meta_from_value->sign_only()); EXPECT_EQ(*meta_from_value, meta); @@ -127,7 +129,8 @@ TEST_F(EthTxStateManagerUnitTest, TxMetaAndValue) { std::make_unique(*Eip2930Transaction::FromTxData( mojom::TxData::New("0x09", "0x4a817c800", "0x5208", "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()), + "0x0de0b6b3a7640000", std::vector(), + false, absl::nullopt), 0x3)); auto* access_list = tx1->access_list(); Eip2930Transaction::AccessListItem item_a; @@ -152,7 +155,8 @@ TEST_F(EthTxStateManagerUnitTest, TxMetaAndValue) { *Eip1559Transaction::FromTxData(mojom::TxData1559::New( mojom::TxData::New("0x09", "0x4a817c800", "0x5208", "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector()), + "0x0de0b6b3a7640000", std::vector(), + false, absl::nullopt), "0x3", "0x1E", "0x32", mojom::GasEstimation1559::New( "0x3b9aca00" /* Hex of 1 * 1e9 */, @@ -170,6 +174,24 @@ TEST_F(EthTxStateManagerUnitTest, TxMetaAndValue) { Eip1559Transaction* tx_from_value2 = static_cast(meta_from_value2->tx()); EXPECT_EQ(*tx_from_value2, *static_cast(meta2.tx())); + + // test sign_only + std::unique_ptr tx3 = std::make_unique( + *EthTransaction::FromTxData(mojom::TxData::New( + "0x09", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt))); + EthTxMeta meta3(std::move(tx3)); + meta3.set_sign_only(true); + base::Value::Dict meta_value3 = meta3.ToValue(); + auto meta_from_value3 = eth_tx_state_manager_->ValueToEthTxMeta(meta_value3); + ASSERT_NE(meta_from_value3, nullptr); + EXPECT_TRUE(meta_from_value3->sign_only()); + meta3.set_sign_only(false); + meta_value3 = meta3.ToValue(); + meta_from_value3 = eth_tx_state_manager_->ValueToEthTxMeta(meta_value3); + ASSERT_NE(meta_from_value3, nullptr); + EXPECT_FALSE(meta_from_value3->sign_only()); } TEST_F(EthTxStateManagerUnitTest, GetTxPrefPathPrefix) { diff --git a/components/brave_wallet/browser/ethereum_keyring_unittest.cc b/components/brave_wallet/browser/ethereum_keyring_unittest.cc index c2f0d84b01a6..1b3fa7eedc1a 100644 --- a/components/brave_wallet/browser/ethereum_keyring_unittest.cc +++ b/components/brave_wallet/browser/ethereum_keyring_unittest.cc @@ -94,10 +94,10 @@ TEST(EthereumKeyringUnitTest, Accounts) { TEST(EthereumKeyringUnitTest, SignTransaction) { // Specific signature check is in eth_transaction_unittest.cc EthereumKeyring keyring; - EthTransaction tx = *EthTransaction::FromTxData( - mojom::TxData::New("0x09", "0x4a817c800", "0x5208", - "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector())); + EthTransaction tx = *EthTransaction::FromTxData(mojom::TxData::New( + "0x09", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt)); keyring.SignTransaction("0xDEADBEEFdeadbeefdeadbeefdeadbeefDEADBEEF", &tx, 0); EXPECT_FALSE(tx.IsSigned()); @@ -220,10 +220,10 @@ TEST(EthereumKeyringUnitTest, ImportedAccounts) { .empty()); // Sign Transaction - EthTransaction tx = *EthTransaction::FromTxData( - mojom::TxData::New("0x09", "0x4a817c800", "0x5208", - "0x3535353535353535353535353535353535353535", - "0x0de0b6b3a7640000", std::vector())); + EthTransaction tx = *EthTransaction::FromTxData(mojom::TxData::New( + "0x09", "0x4a817c800", "0x5208", + "0x3535353535353535353535353535353535353535", "0x0de0b6b3a7640000", + std::vector(), false, absl::nullopt)); keyring.SignTransaction("0xbE93f9BacBcFFC8ee6663f2647917ed7A20a57BB", &tx, 0); EXPECT_FALSE(tx.IsSigned()); diff --git a/components/brave_wallet/browser/ethereum_provider_impl.cc b/components/brave_wallet/browser/ethereum_provider_impl.cc index 23b5d7e8ed23..8ce706fd73de 100644 --- a/components/brave_wallet/browser/ethereum_provider_impl.cc +++ b/components/brave_wallet/browser/ethereum_provider_impl.cc @@ -259,13 +259,14 @@ void EthereumProviderImpl::ContinueGetDefaultKeyringInfo( base::Value id, const std::string& normalized_json_request, const url::Origin& origin, + bool sign_only, mojom::NetworkInfoPtr chain) { keyring_service_->GetKeyringInfo( mojom::kDefaultKeyringId, base::BindOnce(&EthereumProviderImpl::OnGetNetworkAndDefaultKeyringInfo, weak_factory_.GetWeakPtr(), std::move(callback), std::move(id), normalized_json_request, origin, - std::move(chain))); + std::move(chain), sign_only)); } void EthereumProviderImpl::OnGetNetworkAndDefaultKeyringInfo( @@ -274,6 +275,7 @@ void EthereumProviderImpl::OnGetNetworkAndDefaultKeyringInfo( const std::string& normalized_json_request, const url::Origin& origin, mojom::NetworkInfoPtr chain, + bool sign_only, mojom::KeyringInfoPtr keyring_info) { bool reject = false; if (!chain || !keyring_info) { @@ -298,6 +300,7 @@ void EthereumProviderImpl::OnGetNetworkAndDefaultKeyringInfo( "", false); return; } + tx_data_1559->base_data->sign_only = sign_only; if (ShouldCreate1559Tx(tx_data_1559.Clone(), chain->is_eip1559, keyring_info->account_infos, from)) { @@ -977,11 +980,11 @@ void EthereumProviderImpl::CommonRequestOrSendAsync(base::ValueView input_value, // We need to add any method that requires a dialog to interact with. if ((method == kEthRequestAccounts || method == kAddEthereumChainMethod || method == kSwitchEthereumChainMethod || method == kEthSendTransaction || - method == kEthSign || method == kPersonalSign || - method == kPersonalEcRecover || method == kEthSignTypedDataV3 || - method == kEthSignTypedDataV4 || method == kEthGetEncryptionPublicKey || - method == kEthDecrypt || method == kWalletWatchAsset || - method == kRequestPermissionsMethod) && + method == kEthSignTransaction || method == kEthSign || + method == kPersonalSign || method == kPersonalEcRecover || + method == kEthSignTypedDataV3 || method == kEthSignTypedDataV4 || + method == kEthGetEncryptionPublicKey || method == kEthDecrypt || + method == kWalletWatchAsset || method == kRequestPermissionsMethod) && !delegate_->IsTabVisible()) { SendErrorOnRequest( mojom::ProviderError::kResourceUnavailable, @@ -1016,7 +1019,14 @@ void EthereumProviderImpl::CommonRequestOrSendAsync(base::ValueView input_value, base::BindOnce(&EthereumProviderImpl::ContinueGetDefaultKeyringInfo, weak_factory_.GetWeakPtr(), std::move(callback), std::move(id), normalized_json_request, - delegate_->GetOrigin())); + delegate_->GetOrigin(), false)); + } else if (method == kEthSignTransaction) { + json_rpc_service_->GetNetwork( + mojom::CoinType::ETH, + base::BindOnce(&EthereumProviderImpl::ContinueGetDefaultKeyringInfo, + weak_factory_.GetWeakPtr(), std::move(callback), + std::move(id), normalized_json_request, + delegate_->GetOrigin(), true)); } else if (method == kEthSendRawTransaction) { std::string signed_transaction; if (!ParseEthSendRawTransactionParams(normalized_json_request, @@ -1457,6 +1467,7 @@ void EthereumProviderImpl::OnTransactionStatusChanged( mojom::TransactionInfoPtr tx_info) { auto tx_status = tx_info->tx_status; if (tx_status != mojom::TransactionStatus::Submitted && + tx_status != mojom::TransactionStatus::Signed && tx_status != mojom::TransactionStatus::Rejected && tx_status != mojom::TransactionStatus::Error) return; @@ -1472,6 +1483,20 @@ void EthereumProviderImpl::OnTransactionStatusChanged( if (tx_status == mojom::TransactionStatus::Submitted) { formed_response = base::Value(tx_hash); reject = false; + } else if (tx_status == mojom::TransactionStatus::Signed) { + std::string signed_transaction; + if (tx_info->tx_data_union->is_eth_tx_data()) { + DCHECK(tx_info->tx_data_union->get_eth_tx_data()->signed_transaction); + signed_transaction = + *tx_info->tx_data_union->get_eth_tx_data()->signed_transaction; + } else if (tx_info->tx_data_union->is_eth_tx_data_1559()) { + DCHECK(tx_info->tx_data_union->get_eth_tx_data_1559() + ->base_data->signed_transaction); + signed_transaction = *tx_info->tx_data_union->get_eth_tx_data_1559() + ->base_data->signed_transaction; + } + formed_response = base::Value(signed_transaction); + reject = false; } else if (tx_status == mojom::TransactionStatus::Rejected) { formed_response = GetProviderErrorDictionary( mojom::ProviderError::kUserRejectedRequest, diff --git a/components/brave_wallet/browser/ethereum_provider_impl.h b/components/brave_wallet/browser/ethereum_provider_impl.h index 575eaead5f49..8e9926896b10 100644 --- a/components/brave_wallet/browser/ethereum_provider_impl.h +++ b/components/brave_wallet/browser/ethereum_provider_impl.h @@ -256,6 +256,7 @@ class EthereumProviderImpl final base::Value id, const std::string& normalized_json_request, const url::Origin& origin, + bool sign_only, mojom::NetworkInfoPtr chain); void ContinueGetEncryptionPublicKey( RequestCallback callback, @@ -289,6 +290,7 @@ class EthereumProviderImpl final const std::string& normalized_json_request, const url::Origin& origin, mojom::NetworkInfoPtr chain, + bool sign_only, mojom::KeyringInfoPtr keyring_info); // content_settings::Observer: diff --git a/components/brave_wallet/common/BUILD.gn b/components/brave_wallet/common/BUILD.gn index 27b262e0160a..4d4e9d44892b 100644 --- a/components/brave_wallet/common/BUILD.gn +++ b/components/brave_wallet/common/BUILD.gn @@ -3,8 +3,8 @@ import("//brave/components/brave_wallet/common/config.gni") import("//build/buildflag_header.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") -import("//tools/json_schema_compiler/json_schema_api.gni") import("//tools/grit/preprocess_if_expr.gni") +import("//tools/json_schema_compiler/json_schema_api.gni") preprocess_folder = "preprocessed" preprocess_mojo_manifest = "preprocessed_mojo_manifest.json" @@ -108,9 +108,7 @@ generated_types("generated_json_rpc_requests") { sources = [ "json_rpc_requests.idl" ] root_namespace = "brave_wallet::%(namespace)s" deps = [ "//base" ] - visibility = [ - ":common", - ] + visibility = [ ":common" ] } preprocess_if_expr("preprocess_mojo") { diff --git a/components/brave_wallet/common/brave_wallet.mojom b/components/brave_wallet/common/brave_wallet.mojom index ca242982bff2..ae9b5fc753ad 100644 --- a/components/brave_wallet/common/brave_wallet.mojom +++ b/components/brave_wallet/common/brave_wallet.mojom @@ -774,6 +774,8 @@ struct TxData { string to; string value; array data; + bool sign_only; + string? signed_transaction; }; struct GasEstimation1559 { @@ -985,7 +987,8 @@ enum TransactionStatus { Submitted = 3, Confirmed = 4, Error = 5, - Dropped = 6 + Dropped = 6, + Signed = 7 // dapps will submit the transaction }; enum TransactionType { diff --git a/components/brave_wallet/common/web3_provider_constants.h b/components/brave_wallet/common/web3_provider_constants.h index 9c5c7a7f1ba6..652857509696 100644 --- a/components/brave_wallet/common/web3_provider_constants.h +++ b/components/brave_wallet/common/web3_provider_constants.h @@ -32,6 +32,7 @@ constexpr char kEthAccounts[] = "eth_accounts"; constexpr char kEthCoinbase[] = "eth_coinbase"; constexpr char kEthRequestAccounts[] = "eth_requestAccounts"; constexpr char kEthSendTransaction[] = "eth_sendTransaction"; +constexpr char kEthSignTransaction[] = "eth_signTransaction"; constexpr char kEthSendRawTransaction[] = "eth_sendRawTransaction"; constexpr char kEthGetBlockByNumber[] = "eth_getBlockByNumber"; constexpr char kEthBlockNumber[] = "eth_blockNumber"; diff --git a/components/brave_wallet_ui/common/async/lib.ts b/components/brave_wallet_ui/common/async/lib.ts index c65553b9cad5..8c380c41d88e 100644 --- a/components/brave_wallet_ui/common/async/lib.ts +++ b/components/brave_wallet_ui/common/async/lib.ts @@ -949,7 +949,9 @@ export async function sendEthTransaction (payload: SendEthTransactionParams) { gasLimit: payload.gas || '', to: payload.to, value: payload.value, - data: payload.data || [] + data: payload.data || [], + signOnly: false, + signedTransaction: '' } if (isEIP1559) { diff --git a/components/brave_wallet_ui/common/constants/mocks.ts b/components/brave_wallet_ui/common/constants/mocks.ts index 20cc6154aa9d..d2c2dd8c0547 100644 --- a/components/brave_wallet_ui/common/constants/mocks.ts +++ b/components/brave_wallet_ui/common/constants/mocks.ts @@ -24,7 +24,9 @@ export const getMockedTransactionInfo = (): BraveWallet.TransactionInfo => { data: [] as number[], nonce: '0x03', gasLimit: '0x5208', // 2100 - gasPrice: '0x22ecb25c00' // 150 Gwei + gasPrice: '0x22ecb25c00', // 150 Gwei + signOnly: false, + signedTransaction: undefined }, chainId: '1337', maxPriorityFeePerGas: '', diff --git a/components/brave_wallet_ui/components/desktop/portfolio-transaction-item/index.tsx b/components/brave_wallet_ui/components/desktop/portfolio-transaction-item/index.tsx index 3244e9f7e8a2..8278b3f0adbd 100644 --- a/components/brave_wallet_ui/components/desktop/portfolio-transaction-item/index.tsx +++ b/components/brave_wallet_ui/components/desktop/portfolio-transaction-item/index.tsx @@ -78,6 +78,7 @@ const getLocaleKeyForTxStatus = (status: BraveWallet.TransactionStatus) => { case BraveWallet.TransactionStatus.Confirmed: return 'braveWalletTransactionStatusConfirmed' case BraveWallet.TransactionStatus.Error: return 'braveWalletTransactionStatusError' case BraveWallet.TransactionStatus.Dropped: return 'braveWalletTransactionStatusDropped' + case BraveWallet.TransactionStatus.Signed: return 'braveWalletTransactionStatusSigned' default: return '' } } diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx index b54ee1d01ae4..3b918c0dd373 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx +++ b/components/brave_wallet_ui/components/extension/post-confirmation/index.tsx @@ -21,7 +21,7 @@ import * as WalletPanelActions from '../../../panel/actions/wallet_panel_actions // Components import { Panel } from '../index' -import { TransactionSubmitted } from './submitted' +import { TransactionSubmittedOrSigned } from './submitted_or_signed' import { TransactionComplete } from './complete' import { TransactionFailed } from './failed' import { Loader } from './common/common.style' @@ -61,9 +61,10 @@ export function TransactionStatus (props: Props) { ? getLocale('braveWalletButtonClose') : getLocale('braveWalletButtonNext') - if (liveTransaction.txStatus === BraveWallet.TransactionStatus.Submitted) { + if (liveTransaction.txStatus === BraveWallet.TransactionStatus.Submitted || + liveTransaction.txStatus === BraveWallet.TransactionStatus.Signed) { return ( - { const onClose = () => alert('Close panel screen') @@ -21,7 +21,7 @@ export const _TransactionSubmitted = () => { - { ) } -_TransactionSubmitted.story = { - name: 'Transaction Submitted' +_TransactionSubmittedOrSigned.story = { + name: 'Transaction Submitted/Signed' } -export default _TransactionSubmitted +export default _TransactionSubmittedOrSigned diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/submitted/submitted.style.ts b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts similarity index 81% rename from components/brave_wallet_ui/components/extension/post-confirmation/submitted/submitted.style.ts rename to components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts index 658c40ac5081..75ba5a983be4 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/submitted/submitted.style.ts +++ b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts @@ -4,12 +4,12 @@ // you can obtain one at https://mozilla.org/MPL/2.0/. import styled from 'styled-components' -import SubmittedSvg from '../../../../assets/svg-icons/submitted-circle-icon.svg' +import SubmittedOrSignedSvg from '../../../../assets/svg-icons/submitted-circle-icon.svg' import { WalletButton } from '../../../shared/style' import { TransactionStatusIcon, TransactionStatusText } from '../common/common.style' -export const SubmittedIcon = styled(TransactionStatusIcon)` - background: url(${SubmittedSvg}); +export const SubmittedOrSignedIcon = styled(TransactionStatusIcon)` + background: url(${SubmittedOrSignedSvg}); ` export const Title = styled(TransactionStatusText)` diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/submitted/submitted.tsx b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.tsx similarity index 65% rename from components/brave_wallet_ui/components/extension/post-confirmation/submitted/submitted.tsx rename to components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.tsx index 16710c5aad6d..ff9c23b0db01 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/submitted/submitted.tsx +++ b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.tsx @@ -6,7 +6,7 @@ import * as React from 'react' import { useSelector } from 'react-redux' // Constants -import { WalletState, SerializableTransactionInfo } from '../../../../constants/types' +import { BraveWallet, WalletState, SerializableTransactionInfo } from '../../../../constants/types' // Hooks import { useExplorer } from '../../../../common/hooks' @@ -16,7 +16,7 @@ import { getLocale } from '$web-common/locale' // Styled components import { Panel } from '../..' -import { SubmittedIcon, Title } from './submitted.style' +import { SubmittedOrSignedIcon, Title } from './submitted_or_signed.style' import { ButtonRow, DetailButton, @@ -30,7 +30,7 @@ interface Props { onClose: () => void } -export const TransactionSubmitted = (props: Props) => { +export const TransactionSubmittedOrSigned = (props: Props) => { const { headerTitle, transaction, onClose } = props // redux @@ -39,12 +39,21 @@ export const TransactionSubmitted = (props: Props) => { ) const onClickViewOnBlockExplorer = useExplorer(selectedNetwork) + const title = + transaction.txStatus === BraveWallet.TransactionStatus.Submitted + ? getLocale('braveWalletTransactionSubmittedTitle') + : getLocale('braveWalletTransactionSignedTitle') + const description = + transaction.txStatus === BraveWallet.TransactionStatus.Submitted + ? getLocale('braveWalletTransactionSubmittedDescription') + : getLocale('braveWalletTransactionSignedDescription') + return ( - - {getLocale('braveWalletTransactionSubmittedTitle')} + + {title} - {getLocale('braveWalletTransactionSubmittedDescription')} + {description} diff --git a/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts b/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts index 803b413b295f..bdfe92f47de1 100644 --- a/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts +++ b/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts @@ -796,7 +796,7 @@ handler.on(WalletActions.transactionStatusChanged.type, async (store: Store, pay const state = getPanelState(store) const walletState = getWalletState(store) if ( - [BraveWallet.TransactionStatus.Submitted, BraveWallet.TransactionStatus.Rejected, BraveWallet.TransactionStatus.Approved] + [BraveWallet.TransactionStatus.Submitted, BraveWallet.TransactionStatus.Signed, BraveWallet.TransactionStatus.Rejected, BraveWallet.TransactionStatus.Approved] .includes(payload.txInfo.txStatus) ) { if (state.selectedPanel === 'approveTransaction' && walletState.pendingTransactions.length === 0) { diff --git a/components/brave_wallet_ui/stories/locale.ts b/components/brave_wallet_ui/stories/locale.ts index f0cf7ed27dba..da1d9c75ef53 100644 --- a/components/brave_wallet_ui/stories/locale.ts +++ b/components/brave_wallet_ui/stories/locale.ts @@ -640,6 +640,7 @@ provideStrings({ braveWalletTransactionStatusConfirmed: 'Confirmed', braveWalletTransactionStatusError: 'Error', braveWalletTransactionStatusDropped: 'Dropped', + braveWalletTransactionStatusSigned: 'Signed', // NFT Details Page braveWalletNFTDetailBlockchain: 'Blockchain', diff --git a/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts b/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts index 4f2bc43c15cb..473da0d14b02 100644 --- a/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts +++ b/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts @@ -17,7 +17,9 @@ export const mockTransactionInfo: SerializableTransactionInfo = { gasLimit: '21000', to: '2', value: '0x15ddf09c97b0000', - data: Array.from(new Uint8Array(24)) + data: Array.from(new Uint8Array(24)), + signOnly: false, + signedTransaction: undefined }, chainId: '0x0', maxPriorityFeePerGas: '', diff --git a/components/brave_wallet_ui/stories/wallet-extension-panels.tsx b/components/brave_wallet_ui/stories/wallet-extension-panels.tsx index 017891a466e0..c744955e56f3 100644 --- a/components/brave_wallet_ui/stories/wallet-extension-panels.tsx +++ b/components/brave_wallet_ui/stories/wallet-extension-panels.tsx @@ -106,7 +106,9 @@ const transactionDummyData: AccountTransactions = { gasPrice: '0x20000000000', nonce: '0x1', to: 'ETHEREUM ACCOUNT 2', - value: '0xb1a2bc2ec50000' + value: '0xb1a2bc2ec50000', + signOnly: false, + signedTransaction: undefined }, chainId: '', maxFeePerGas: '', @@ -139,7 +141,9 @@ const transactionDummyData: AccountTransactions = { gasPrice: '0x20000000000', nonce: '0x1', to: '0xcd3a3f8e0e4bdc174c9e2e63b4c22e15a7f7f92a', - value: '0xb1a2bc2ec50000' + value: '0xb1a2bc2ec50000', + signOnly: false, + signedTransaction: undefined }, chainId: '', maxFeePerGas: '', @@ -172,7 +176,9 @@ const transactionDummyData: AccountTransactions = { gasPrice: '0x20000000000', nonce: '0x1', to: '0x7d66c9ddAED3115d93Bd1790332f3Cd06Cf52B14', - value: '0xb1a2bc2ec90000' + value: '0xb1a2bc2ec90000', + signOnly: false, + signedTransaction: undefined }, chainId: '', maxFeePerGas: '', @@ -205,7 +211,9 @@ const transactionDummyData: AccountTransactions = { gasPrice: '0x20000000000', nonce: '0x1', to: '0xcd3a3f8e0e4bdc174c9e2e63b4c22e15a7f7f92a', - value: '0xb1a2bc2ec90000' + value: '0xb1a2bc2ec90000', + signOnly: false, + signedTransaction: undefined }, chainId: '', maxFeePerGas: '', @@ -238,7 +246,9 @@ const transactionDummyData: AccountTransactions = { gasPrice: '0x20000000000', nonce: '0x1', to: '0xcd3a3f8e0e4bdc174c9e2e63b4c22e15a7f7f92a', - value: '0xb1a2bc2ec90000' + value: '0xb1a2bc2ec90000', + signOnly: false, + signedTransaction: undefined }, chainId: '', maxFeePerGas: '', @@ -273,7 +283,9 @@ const transactionDummyData: AccountTransactions = { gasPrice: '0x20000000000', nonce: '0x1', to: '0xcd3a3f8e0e4bdc174c9e2e63b4c22e15a7f7f92a', - value: '0xb1a2bc2ec90000' + value: '0xb1a2bc2ec90000', + signOnly: false, + signedTransaction: undefined }, chainId: '', maxFeePerGas: '', @@ -306,7 +318,9 @@ const transactionDummyData: AccountTransactions = { gasPrice: '0x20000000000', nonce: '0x1', to: '0xcd3a3f8e0e4bdc174c9e2e63b4c22e15a7f7f92a', - value: '0xb1a2bc2ec90000' + value: '0xb1a2bc2ec90000', + signOnly: false, + signedTransaction: undefined }, chainId: '', maxFeePerGas: '', diff --git a/components/brave_wallet_ui/utils/tx-utils.ts b/components/brave_wallet_ui/utils/tx-utils.ts index 704cff236771..6b09f888c0ca 100644 --- a/components/brave_wallet_ui/utils/tx-utils.ts +++ b/components/brave_wallet_ui/utils/tx-utils.ts @@ -78,6 +78,8 @@ export const getTransactionStatusString = (statusId: number) => { return getLocale('braveWalletTransactionStatusError') case BraveWallet.TransactionStatus.Dropped: return getLocale('braveWalletTransactionStatusDropped') + case BraveWallet.TransactionStatus.Signed: + return getLocale('braveWalletTransactionStatusSigned') default: return '' } diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp index 32b69dabaf73..ca32d74d195b 100644 --- a/components/resources/wallet_strings.grdp +++ b/components/resources/wallet_strings.grdp @@ -375,6 +375,7 @@ Confirmed Error Dropped + Signed Recent transactions Transaction details Date @@ -510,6 +511,8 @@ All Accounts Transaction submitted Transaction has been successfully sent to the network and awaits confirmation. + Transaction signed + Transaction has been signed and will be sent to network by dapps and awaits confirmation $11 ETH was returned to your wallet Transaction failed Transaction was failed due to a large price movement. Increase slippage tolerance to succeed at a larger price movement. From 918d11b1374aa9b96fe1ee3eb39ac926b660810e Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Mon, 5 Dec 2022 16:00:12 -0800 Subject: [PATCH 4/7] Update tests --- android/android_browser_tests.gni | 2 +- .../eth_pending_tx_tracker_unittest.cc | 11 +- ...> send_or_sign_transaction_browsertest.cc} | 400 ++++++++++++------ .../browser/eth_nonce_tracker_unittest.cc | 7 + .../brave_wallet_ui/utils/tx-utils.test.ts | 9 +- test/BUILD.gn | 2 +- ...ion.html => send_or_sign_transaction.html} | 36 +- 7 files changed, 325 insertions(+), 142 deletions(-) rename browser/brave_wallet/{send_transaction_browsertest.cc => send_or_sign_transaction_browsertest.cc} (66%) rename test/data/brave-wallet/{send_transaction.html => send_or_sign_transaction.html} (65%) diff --git a/android/android_browser_tests.gni b/android/android_browser_tests.gni index c3bc5d870982..9069b0704cff 100644 --- a/android/android_browser_tests.gni +++ b/android/android_browser_tests.gni @@ -24,7 +24,7 @@ android_test_exception_sources = [ "//brave/browser/brave_wallet/brave_wallet_sign_message_browsertest.cc", "//brave/browser/brave_wallet/brave_wallet_tab_helper_browsertest.cc", "//brave/browser/brave_wallet/ethereum_provider_browsertest.cc", - "//brave/browser/brave_wallet/send_transaction_browsertest.cc", + "//brave/browser/brave_wallet/send_or_sign_transaction_browsertest.cc", "//brave/browser/brave_wallet/solana_provider_browsertest.cc", "//brave/browser/brave_wallet/solana_provider_renderer_browsertest.cc", "//brave/browser/brave_wallet/wallet_watch_asset_browsertest.cc", diff --git a/browser/brave_wallet/eth_pending_tx_tracker_unittest.cc b/browser/brave_wallet/eth_pending_tx_tracker_unittest.cc index 9be2d6b0a9bf..d4162119df3d 100644 --- a/browser/brave_wallet/eth_pending_tx_tracker_unittest.cc +++ b/browser/brave_wallet/eth_pending_tx_tracker_unittest.cc @@ -172,8 +172,13 @@ TEST_F(EthPendingTxTrackerUnitTest, UpdatePendingTransactions) { tx_state_manager.AddOrUpdateTx(meta); meta.set_id("004"); meta.set_from(addr2); + meta.tx()->set_nonce(uint256_t(4)); + meta.set_status(mojom::TransactionStatus::Signed); + tx_state_manager.AddOrUpdateTx(meta); + meta.set_id("005"); + meta.set_from(addr2); meta.tx()->set_nonce(uint256_t(5)); - meta.set_status(mojom::TransactionStatus::Submitted); + meta.set_status(mojom::TransactionStatus::Signed); tx_state_manager.AddOrUpdateTx(meta); test_url_loader_factory()->SetInterceptor( @@ -200,7 +205,7 @@ TEST_F(EthPendingTxTrackerUnitTest, UpdatePendingTransactions) { size_t num_pending; EXPECT_TRUE(pending_tx_tracker.UpdatePendingTransactions(&num_pending)); - EXPECT_EQ(3UL, num_pending); + EXPECT_EQ(4UL, num_pending); WaitForResponse(); auto meta_from_state = tx_state_manager.GetEthTx("001"); ASSERT_NE(meta_from_state, nullptr); @@ -212,6 +217,8 @@ TEST_F(EthPendingTxTrackerUnitTest, UpdatePendingTransactions) { meta_from_state = tx_state_manager.GetEthTx("003"); ASSERT_EQ(meta_from_state, nullptr); meta_from_state = tx_state_manager.GetEthTx("004"); + ASSERT_EQ(meta_from_state, nullptr); + meta_from_state = tx_state_manager.GetEthTx("005"); ASSERT_NE(meta_from_state, nullptr); EXPECT_EQ(meta_from_state->status(), mojom::TransactionStatus::Confirmed); EXPECT_EQ(meta_from_state->tx_receipt().contract_address, diff --git a/browser/brave_wallet/send_transaction_browsertest.cc b/browser/brave_wallet/send_or_sign_transaction_browsertest.cc similarity index 66% rename from browser/brave_wallet/send_transaction_browsertest.cc rename to browser/brave_wallet/send_or_sign_transaction_browsertest.cc index 97ce63121137..00d86ec1fe0a 100644 --- a/browser/brave_wallet/send_transaction_browsertest.cc +++ b/browser/brave_wallet/send_or_sign_transaction_browsertest.cc @@ -43,6 +43,7 @@ #include "content/public/test/test_utils.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" #include "url/origin.h" @@ -64,6 +65,11 @@ std::unique_ptr HandleRequest( return std::move(http_response); } +constexpr char kSignedTransaction[] = + "0xf8688296048525f38e9e0082960494084dcb94038af1715963f149079ce011c4b2296211" + "80820a95a0c58904f26f5ac0e86a292d9a832bbb56ab8d7bfb9f74a5eafa99778bf059ea93" + "a07db1772583c02ae58637916c03a3a1d9fd98044dd83c52da6870fee25a8575e1"; + } // namespace namespace brave_wallet { @@ -112,16 +118,16 @@ class TestTxServiceObserver : public brave_wallet::mojom::TxServiceObserver { bool expect_eip1559_tx_ = false; }; -class SendTransactionBrowserTest : public InProcessBrowserTest { +class SendOrSignTransactionBrowserTest : public InProcessBrowserTest { public: - SendTransactionBrowserTest() + SendOrSignTransactionBrowserTest() : https_server_for_files_(net::EmbeddedTestServer::TYPE_HTTPS), https_server_for_rpc_(net::EmbeddedTestServer::TYPE_HTTPS) { scoped_feature_list_.InitAndEnableFeature( brave_wallet::features::kNativeBraveWalletFeature); } - ~SendTransactionBrowserTest() override = default; + ~SendOrSignTransactionBrowserTest() override = default; void SetUpCommandLine(base::CommandLine* command_line) override { InProcessBrowserTest::SetUpCommandLine(command_line); @@ -335,20 +341,22 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { run_loop.Run(); } - void WaitForSendTransactionResultReady() { + void WaitForSendOrSignTransactionResultReady() { content::DOMMessageQueue message_queue; std::string message; EXPECT_TRUE(message_queue.WaitForMessage(&message)); EXPECT_EQ("\"result ready\"", message); } - void TestUserApproved(const std::string& test_method, + void TestUserApproved(absl::optional expected_signed_tx, + const std::string& test_method, const std::string& data = "", bool skip_restore = false) { if (!skip_restore) RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + bool sign_only = expected_signed_tx.has_value(); + GURL url = https_server_for_files()->GetURL( + "a.com", "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); @@ -357,9 +365,10 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { ASSERT_TRUE(ExecJs( web_contents(), base::StringPrintf( - "sendTransaction(%s, '%s', " + "sendOrSignTransaction(%s, %s, '%s', " "'0x084DCb94038af1715963F149079cE011C4B22961', " "'0x084DCb94038af1715963F149079cE011C4B22962', '0x11', '%s');", + sign_only ? "true" : "false", observer()->expect_eip1559_tx() ? "true" : "false", test_method.c_str(), data.c_str()))); observer()->WaitForNewUnapprovedTx(); @@ -386,23 +395,34 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { EXPECT_EQ(1UL, infos.size()); EXPECT_TRUE( base::EqualsCaseInsensitiveASCII(from(), infos[0]->from_address)); - EXPECT_EQ(mojom::TransactionStatus::Submitted, infos[0]->tx_status); + if (sign_only) { + EXPECT_EQ(mojom::TransactionStatus::Signed, infos[0]->tx_status); + } else { + EXPECT_EQ(mojom::TransactionStatus::Submitted, infos[0]->tx_status); + } EXPECT_FALSE(infos[0]->tx_hash.empty()); ASSERT_TRUE(infos[0]->tx_data_union->is_eth_tx_data_1559()); EXPECT_EQ(infos[0]->tx_data_union->get_eth_tx_data_1559()->base_data->nonce, "0x9604"); - WaitForSendTransactionResultReady(); - EXPECT_EQ(EvalJs(web_contents(), "getSendTransactionResult()", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractString(), - "0x00000000000009604"); + WaitForSendOrSignTransactionResultReady(); + if (sign_only) { + EXPECT_EQ(EvalJs(web_contents(), "getSendOrSignTransactionResult()", + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractString(), + *expected_signed_tx); + } else { + EXPECT_EQ(EvalJs(web_contents(), "getSendOrSignTransactionResult()", + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractString(), + "0x00000000000009604"); + } } - void TestUserRejected(const std::string& test_method) { + void TestUserRejected(bool sign_only, const std::string& test_method) { RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL( + "a.com", "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); @@ -411,10 +431,10 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { ASSERT_TRUE( ExecJs(web_contents(), base::StringPrintf( - "sendTransaction(false, '%s', " + "sendOrSignTransaction(%s, false, '%s', " "'0x084DCb94038af1715963F149079cE011C4B22961', " "'0x084DCb94038af1715963F149079cE011C4B22962', '0x11');", - test_method.c_str()))); + sign_only ? "true" : "false", test_method.c_str()))); observer()->WaitForNewUnapprovedTx(); base::RunLoop().RunUntilIdle(); EXPECT_TRUE( @@ -446,8 +466,8 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { ->tx_data_union->get_eth_tx_data_1559() ->base_data->nonce.empty()); - WaitForSendTransactionResultReady(); - EXPECT_EQ(EvalJs(web_contents(), "getSendTransactionError()", + WaitForSendOrSignTransactionResultReady(); + EXPECT_EQ(EvalJs(web_contents(), "getSendOrSignTransactionError()", content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) .ExtractString(), l10n_util::GetStringUTF8( @@ -468,10 +488,11 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { return transaction_infos; } - void TestSendTransactionError(const std::string& test_method) { + void TestSendTransactionError(bool sign_only, + const std::string& test_method) { RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL( + "a.com", "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); @@ -480,14 +501,14 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { ASSERT_TRUE( ExecJs(web_contents(), base::StringPrintf( - "sendTransaction(false, '%s', " + "sendOrSignTransaction(%s, false, '%s', " "'0x084DCb94038af1715963F149079cE011C4B22961', " "'0x084DCb94038af1715963F149079cE011C4B22962', '0x11', " "'invalid');", - test_method.c_str()))); + sign_only ? "true" : "false", test_method.c_str()))); - WaitForSendTransactionResultReady(); - EXPECT_EQ(EvalJs(web_contents(), "getSendTransactionError()", + WaitForSendOrSignTransactionResultReady(); + EXPECT_EQ(EvalJs(web_contents(), "getSendOrSignTransactionError()", content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) .ExtractString(), "Internal JSON-RPC error"); @@ -518,128 +539,234 @@ class SendTransactionBrowserTest : public InProcessBrowserTest { std::string chain_id_; }; -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserApprovedRequest) { - TestUserApproved("request"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedRequestSend) { + TestUserApproved(absl::nullopt, "request"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserApprovedSend1) { - TestUserApproved("send1"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, UserApprovedSend1) { + TestUserApproved(absl::nullopt, "send1"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserApprovedSend2) { - TestUserApproved("send2"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, UserApprovedSend2) { + TestUserApproved(absl::nullopt, "send2"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserApprovedSendAsync) { - TestUserApproved("sendAsync"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSendAsync) { + TestUserApproved(absl::nullopt, "sendAsync"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserApprovedRequestData0x) { - TestUserApproved("request", "0x"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedRequestData0x) { + TestUserApproved(absl::nullopt, "request", "0x"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserApprovedSend1Data0x) { - TestUserApproved("send1", "0x1"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSend1Data0x) { + TestUserApproved(absl::nullopt, "send1", "0x1"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserApprovedSend2Data0x) { - TestUserApproved("send2", "0x11"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSend2Data0x) { + TestUserApproved(absl::nullopt, "send2", "0x11"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, UserApprovedSendAsyncData0x) { - TestUserApproved("sendAsync", "0x"); + TestUserApproved(absl::nullopt, "sendAsync", "0x"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserRejectedRequest) { - TestUserRejected("request"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, UserRejectedRequest) { + TestUserRejected(false, "request"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserRejectedSend1) { - TestUserRejected("send1"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, UserRejectedSend1) { + TestUserRejected(false, "send1"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserRejectedSend2) { - TestUserRejected("send2"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, UserRejectedSend2) { + TestUserRejected(false, "send2"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, UserRejectedSendAsync) { - TestUserRejected("sendAsync"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserRejectedSendAsync) { + TestUserRejected(false, "sendAsync"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, SendTransactionErrorRequest) { - TestSendTransactionError("request"); + TestSendTransactionError(false, "request"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, SendTransactionErrorSend1) { - TestSendTransactionError("send1"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + SendTransactionErrorSend1) { + TestSendTransactionError(false, "send1"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, SendTransactionErrorSend2) { - TestSendTransactionError("send2"); +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + SendTransactionErrorSend2) { + TestSendTransactionError(false, "send2"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, SendTransactionErrorSendAsync) { - TestSendTransactionError("sendAsync"); + TestSendTransactionError(false, "sendAsync"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedRequestSign) { + TestUserApproved(kSignedTransaction, "request"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSend1Sign) { + TestUserApproved(kSignedTransaction, "send1"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSend2Sign) { + TestUserApproved(kSignedTransaction, "send2"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSendAsyncSign) { + TestUserApproved(kSignedTransaction, "sendAsync"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedRequestData0xSign) { + TestUserApproved(kSignedTransaction, "request", "0x"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSend1Data0xSign) { + TestUserApproved( + "0xf8688296048525f38e9e0082960494084dcb94038af1715963f149079ce011c4b22962" + "1101820a95a0ea3d09b65bb17424978c9ec3c9319c157523374dde70025b52034ae33f85" + "82a8a02a879841219186d6d1029d674a6ad428e5e6693ac6b92304905fcaae533d69a3", + "send1", "0x1"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSend2Data0xSign) { + TestUserApproved( + "0xf8688296048525f38e9e0082960494084dcb94038af1715963f149079ce011c4b22962" + "1111820a96a0fe7acb8944ff3223ddb123ac046129093998087d9895203cb472ed865b6a" + "7213a071581e1fd537e114e7416322c06857f38df4e1f91e1abc9adb7cfb5840eaabca", + "send2", "0x11"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, InvalidAddress) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserApprovedSendAsyncData0xSign) { + TestUserApproved(kSignedTransaction, "sendAsync", "0x"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserRejectedRequestSign) { + TestUserRejected(true, "request"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserRejectedSend1Sign) { + TestUserRejected(true, "send1"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserRejectedSend2Sign) { + TestUserRejected(true, "send2"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + UserRejectedSendAsyncSign) { + TestUserRejected(true, "sendAsync"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + SignTransactionErrorRequest) { + TestSendTransactionError(true, "request"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + SignTransactionErrorSend1) { + TestSendTransactionError(true, "send1"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + SignTransactionErrorSend2) { + TestSendTransactionError(true, "send2"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + SignTransactionErrorSendAsync) { + TestSendTransactionError(true, "sendAsync"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, InvalidAddress) { RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); CallEthereumEnable(); UserGrantPermission(true); - ASSERT_TRUE(ExecJs(web_contents(), - "sendTransaction(false, 'request', " - "'0x6b1Bd828cF8CE051B6282dCFEf6863746E2E1909', " - "'0x084DCb94038af1715963F149079cE011C4B22962', '0x11');")); - - WaitForSendTransactionResultReady(); - EXPECT_FALSE( - brave_wallet::BraveWalletTabHelper::FromWebContents(web_contents()) - ->IsShowingBubble()); - EXPECT_EQ(EvalJs(web_contents(), "getSendTransactionError()", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractString(), - l10n_util::GetStringUTF8( - IDS_WALLET_ETH_SEND_TRANSACTION_FROM_NOT_AUTHED)); + for (bool sign_only : {true, false}) { + ASSERT_TRUE( + ExecJs(web_contents(), + base::StringPrintf( + "sendOrSignTransaction(%s, false, 'request', " + "'0x6b1Bd828cF8CE051B6282dCFEf6863746E2E1909', " + "'0x084DCb94038af1715963F149079cE011C4B22962', '0x11');", + sign_only ? "true" : "false"))); + + WaitForSendOrSignTransactionResultReady(); + EXPECT_FALSE( + brave_wallet::BraveWalletTabHelper::FromWebContents(web_contents()) + ->IsShowingBubble()); + EXPECT_EQ(EvalJs(web_contents(), "getSendOrSignTransactionError()", + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractString(), + l10n_util::GetStringUTF8( + IDS_WALLET_ETH_SEND_TRANSACTION_FROM_NOT_AUTHED)); + } } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, NoEthPermission) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, NoEthPermission) { RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); CallEthereumEnable(); UserGrantPermission(false); - ASSERT_TRUE(ExecJs(web_contents(), - "sendTransaction(false, 'request', " - "'0x084DCb94038af1715963F149079cE011C4B22961', " - "'0x084DCb94038af1715963F149079cE011C4B22962', '0x11');")); - - WaitForSendTransactionResultReady(); - EXPECT_FALSE( - brave_wallet::BraveWalletTabHelper::FromWebContents(web_contents()) - ->IsShowingBubble()); - EXPECT_EQ(EvalJs(web_contents(), "getSendTransactionError()", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractString(), - l10n_util::GetStringUTF8( - IDS_WALLET_ETH_SEND_TRANSACTION_FROM_NOT_AUTHED)); + for (bool sign_only : {true, false}) { + ASSERT_TRUE( + ExecJs(web_contents(), + base::StringPrintf( + "sendOrSignTransaction(%s, false, 'request', " + "'0x084DCb94038af1715963F149079cE011C4B22961', " + "'0x084DCb94038af1715963F149079cE011C4B22962', '0x11');", + sign_only ? "true" : "false"))); + + WaitForSendOrSignTransactionResultReady(); + EXPECT_FALSE( + brave_wallet::BraveWalletTabHelper::FromWebContents(web_contents()) + ->IsShowingBubble()); + EXPECT_EQ(EvalJs(web_contents(), "getSendOrSignTransactionError()", + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractString(), + l10n_util::GetStringUTF8( + IDS_WALLET_ETH_SEND_TRANSACTION_FROM_NOT_AUTHED)); + } } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, SelectedAddress) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, SelectedAddress) { RestoreWallet(); AddAccount("account 2"); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); @@ -684,10 +811,10 @@ IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, SelectedAddress) { from(1)); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, NetworkVersion) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, NetworkVersion) { RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); @@ -732,10 +859,10 @@ IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, NetworkVersion) { "undefined"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, IsUnlocked) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, IsUnlocked) { RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); @@ -752,10 +879,10 @@ IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, IsUnlocked) { .ExtractBool()); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, IsConnected) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, IsConnected) { RestoreWallet(); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); EXPECT_TRUE(EvalJs(web_contents(), "getIsConnected()", @@ -763,20 +890,21 @@ IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, IsConnected) { .ExtractBool()); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, EthSendTransactionEIP1559Tx) { SetNetworkForTesting("0x1"); // mainnet observer()->SetExpectEip1559Tx(true); - TestUserApproved("request"); + TestUserApproved(absl::nullopt, "request"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, EthSendTransactionLegacyTx) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + EthSendTransactionLegacyTx) { SetNetworkForTesting("0x539"); // localhost observer()->SetExpectEip1559Tx(false); - TestUserApproved("request"); + TestUserApproved(absl::nullopt, "request"); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, EthSendTransactionCustomNetworkLegacyTx) { SetNetworkForTesting("0x5566"); observer()->SetExpectEip1559Tx(false); @@ -785,14 +913,50 @@ IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, mojom::NetworkInfo chain = GetTestNetworkInfo1("0x5566"); AddCustomNetwork(browser()->profile()->GetPrefs(), chain); - TestUserApproved("request", "", true /* skip_restore */); + TestUserApproved(absl::nullopt, "request", "", true /* skip_restore */); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + EthSignTransactionEIP1559Tx) { + SetNetworkForTesting("0x1"); // mainnet + observer()->SetExpectEip1559Tx(true); + TestUserApproved( + "0x02f86d0182960484f38e9e008525f38e9e0082960494084dcb94038af1715963f14907" + "9ce011c4b229621180c001a0e152033adac7e7316007446c0cd45b97a21911b4e414b087" + "2d0f207dd9ac4226a07ed1a15909a925716d97ab6e2c7077c7b4b0616c8bc522bcd4914a" + "79ef5e6d1d", + "request"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + EthSignTransactionLegacyTx) { + SetNetworkForTesting("0x539"); // localhost + observer()->SetExpectEip1559Tx(false); + TestUserApproved(kSignedTransaction, "request"); +} + +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + EthSignTransactionCustomNetworkLegacyTx) { + SetNetworkForTesting("0x5566"); + observer()->SetExpectEip1559Tx(false); + RestoreWallet(); + + mojom::NetworkInfo chain = GetTestNetworkInfo1("0x5566"); + AddCustomNetwork(browser()->profile()->GetPrefs(), chain); + + TestUserApproved( + "0xf8688296048525f38e9e0082960494084dcb94038af1715963f149079ce011c4b22962" + "118082aaf0a01789b12329c3b46db7bc23af14df45ebc54f6ce8da40f4db4cec866c73bf" + "2ed5a0642ca22062c10f05bfce787107c44001ed8bf1a1d8416cf2e9b133aadbc88076", + "request", "", true /* skip_restore */); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, SecondEnableCallFails) { +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, + SecondEnableCallFails) { RestoreWallet(); AddAccount("account 2"); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); @@ -813,12 +977,12 @@ IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, SecondEnableCallFails) { true); } -IN_PROC_BROWSER_TEST_F(SendTransactionBrowserTest, +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, EnableCallRequestsUnlockIfLocked) { RestoreWallet(); AddAccount("account 2"); - GURL url = - https_server_for_files()->GetURL("a.com", "/send_transaction.html"); + GURL url = https_server_for_files()->GetURL("a.com", + "/send_or_sign_transaction.html"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); EXPECT_TRUE(WaitForLoadStop(web_contents())); diff --git a/components/brave_wallet/browser/eth_nonce_tracker_unittest.cc b/components/brave_wallet/browser/eth_nonce_tracker_unittest.cc index 62bbb56824e8..015bafc25527 100644 --- a/components/brave_wallet/browser/eth_nonce_tracker_unittest.cc +++ b/components/brave_wallet/browser/eth_nonce_tracker_unittest.cc @@ -138,6 +138,13 @@ TEST_F(EthNonceTrackerUnitTest, GetNonce) { tx_state_manager.AddOrUpdateTx(meta); GetNextNonce(&nonce_tracker, address, true, 5); + + // tx count: 2, confirmed: [2, 3], pending: [4, 4], sign: [5] + meta.set_status(mojom::TransactionStatus::Signed); + meta.set_id(TxMeta::GenerateMetaID()); + tx_state_manager.AddOrUpdateTx(meta); + + GetNextNonce(&nonce_tracker, address, true, 5); } TEST_F(EthNonceTrackerUnitTest, NonceLock) { diff --git a/components/brave_wallet_ui/utils/tx-utils.test.ts b/components/brave_wallet_ui/utils/tx-utils.test.ts index 06a8b59a80bb..d27b802efaec 100644 --- a/components/brave_wallet_ui/utils/tx-utils.test.ts +++ b/components/brave_wallet_ui/utils/tx-utils.test.ts @@ -32,7 +32,12 @@ describe('Check Transaction Status Strings Value', () => { expect(getTransactionStatusString(6)).toEqual('braveWalletTransactionStatusDropped') }) - test('Transaction ID 7 should return an empty string', () => { - expect(getTransactionStatusString(7)).toEqual('') + test('Transaction ID 7 should return Signed', () => { + expect(getTransactionStatusString(7)) + .toEqual('braveWalletTransactionStatusSigned') + }) + + test('Transaction ID 8 should return an empty string', () => { + expect(getTransactionStatusString(8)).toEqual('') }) }) diff --git a/test/BUILD.gn b/test/BUILD.gn index bbbc18b667af..a250fea66710 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -699,7 +699,7 @@ test("brave_browser_tests") { "//brave/browser/brave_wallet/brave_wallet_sign_message_browsertest.cc", "//brave/browser/brave_wallet/brave_wallet_tab_helper_browsertest.cc", "//brave/browser/brave_wallet/ethereum_provider_browsertest.cc", - "//brave/browser/brave_wallet/send_transaction_browsertest.cc", + "//brave/browser/brave_wallet/send_or_sign_transaction_browsertest.cc", "//brave/browser/brave_wallet/solana_provider_browsertest.cc", "//brave/browser/brave_wallet/solana_provider_renderer_browsertest.cc", "//brave/browser/brave_wallet/wallet_watch_asset_browsertest.cc", diff --git a/test/data/brave-wallet/send_transaction.html b/test/data/brave-wallet/send_or_sign_transaction.html similarity index 65% rename from test/data/brave-wallet/send_transaction.html rename to test/data/brave-wallet/send_or_sign_transaction.html index ab34721bcbdc..2d9a17237568 100644 --- a/test/data/brave-wallet/send_transaction.html +++ b/test/data/brave-wallet/send_or_sign_transaction.html @@ -15,8 +15,8 @@ window.domAutomationController.send(permissionGranted) } - var sendTransactionResult, sendTransactionError - function sendTransaction(is1559, method, from, to, value, data) { + var sendOrSignTransactionResult, sendOrSignTransactionError + function sendOrSignTransaction(signOnly, is1559, method, from, to, value, data) { const params = [{ from, to, @@ -38,52 +38,52 @@ if (method == 'request') { window.ethereum.request({ - method: 'eth_sendTransaction', + method: signOnly ? 'eth_signTransaction' : 'eth_sendTransaction', params }).then(result => { - sendTransactionResult = result + sendOrSignTransactionResult = result window.domAutomationController.send('result ready') }).catch(error => { - sendTransactionError = error.message + sendOrSignTransactionError = error.message window.domAutomationController.send('result ready') }) } else if (method == 'send1') { window.ethereum.send({ id: 'hello', - method: 'eth_sendTransaction', + method: signOnly ? 'eth_signTransaction' : 'eth_sendTransaction', params }, (err, result) => { - sendTransactionError = err && err.error ? err.error.message : undefined - sendTransactionResult = result ? result.result : undefined + sendOrSignTransactionError = err && err.error ? err.error.message : undefined + sendOrSignTransactionResult = result ? result.result : undefined window.domAutomationController.send('result ready') }) } else if (method == 'send2') { - window.ethereum.send('eth_sendTransaction', params) + window.ethereum.send(signOnly ? 'eth_signTransaction' : 'eth_sendTransaction', params) .then(result => { - sendTransactionResult = result.result + sendOrSignTransactionResult = result.result window.domAutomationController.send('result ready') }).catch(error => { - sendTransactionError = error.error.message + sendOrSignTransactionError = error.error.message window.domAutomationController.send('result ready') }) } else if (method == 'sendAsync') { window.ethereum.sendAsync({ id: 'hello', - method: 'eth_sendTransaction', + method: signOnly ? 'eth_signTransaction' : 'eth_sendTransaction', params }, (err, result) => { - sendTransactionError = err && err.error ? err.error.message : undefined - sendTransactionResult = result ? result.result : undefined + sendOrSignTransactionError = err && err.error ? err.error.message : undefined + sendOrSignTransactionResult = result ? result.result : undefined window.domAutomationController.send('result ready') }) } } - function getSendTransactionResult() { - window.domAutomationController.send(sendTransactionResult) + function getSendOrSignTransactionResult() { + window.domAutomationController.send(sendOrSignTransactionResult) } - function getSendTransactionError() { - window.domAutomationController.send(sendTransactionError) + function getSendOrSignTransactionError() { + window.domAutomationController.send(sendOrSignTransactionError) } // window.ethereum.selectedAddress and not necessarily the selected account function getSelectedAddress() { From 3454d930fb21fa471063aa512969cd807f9f81c4 Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Mon, 5 Dec 2022 17:43:12 -0800 Subject: [PATCH 5/7] presubmit fix --- .../brave_wallet/send_or_sign_transaction_browsertest.cc | 6 +++--- components/brave_wallet/common/BUILD.gn | 5 +++++ .../submitted_or_signed/submitted_or_signed.style.ts | 3 ++- .../panel/async/wallet_panel_async_handler.ts | 5 ++++- test/BUILD.gn | 5 +++++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/browser/brave_wallet/send_or_sign_transaction_browsertest.cc b/browser/brave_wallet/send_or_sign_transaction_browsertest.cc index 00d86ec1fe0a..1f27e26f21c8 100644 --- a/browser/brave_wallet/send_or_sign_transaction_browsertest.cc +++ b/browser/brave_wallet/send_or_sign_transaction_browsertest.cc @@ -1,7 +1,7 @@ /* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */ + * You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "base/command_line.h" #include "base/feature_list.h" @@ -827,7 +827,7 @@ IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, NetworkVersion) { EXPECT_EQ(EvalJs(web_contents(), "getNetworkVersion()", content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) .ExtractString(), - std::to_string((uint64_t)chain_id_uint256)); + base::NumberToString((uint64_t)chain_id_uint256)); // Newly added network change std::string chain_id = "0x38"; @@ -841,7 +841,7 @@ IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, NetworkVersion) { EXPECT_EQ(EvalJs(web_contents(), "getNetworkVersion()", content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) .ExtractString(), - std::to_string((uint64_t)chain_id_uint256)); + base::NumberToString((uint64_t)chain_id_uint256)); // Make sure chainId > uint64_t has networkVersion undefined. This is // just a current limitation that we will likely get rid of in the future. diff --git a/components/brave_wallet/common/BUILD.gn b/components/brave_wallet/common/BUILD.gn index 4d4e9d44892b..c48c4ba48362 100644 --- a/components/brave_wallet/common/BUILD.gn +++ b/components/brave_wallet/common/BUILD.gn @@ -1,3 +1,8 @@ +# 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/. + import("//brave/build/config.gni") import("//brave/components/brave_wallet/common/config.gni") import("//build/buildflag_header.gni") diff --git a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts index 75ba5a983be4..24c4f542303d 100644 --- a/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts +++ b/components/brave_wallet_ui/components/extension/post-confirmation/submitted_or_signed/submitted_or_signed.style.ts @@ -1,7 +1,8 @@ // 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/. +// You can obtain one at https://mozilla.org/MPL/2.0/. + import styled from 'styled-components' import SubmittedOrSignedSvg from '../../../../assets/svg-icons/submitted-circle-icon.svg' diff --git a/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts b/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts index bdfe92f47de1..b43f5505081b 100644 --- a/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts +++ b/components/brave_wallet_ui/panel/async/wallet_panel_async_handler.ts @@ -796,7 +796,10 @@ handler.on(WalletActions.transactionStatusChanged.type, async (store: Store, pay const state = getPanelState(store) const walletState = getWalletState(store) if ( - [BraveWallet.TransactionStatus.Submitted, BraveWallet.TransactionStatus.Signed, BraveWallet.TransactionStatus.Rejected, BraveWallet.TransactionStatus.Approved] + [BraveWallet.TransactionStatus.Submitted, + BraveWallet.TransactionStatus.Signed, + BraveWallet.TransactionStatus.Rejected, + BraveWallet.TransactionStatus.Approved] .includes(payload.txInfo.txStatus) ) { if (state.selectedPanel === 'approveTransaction' && walletState.pendingTransactions.length === 0) { diff --git a/test/BUILD.gn b/test/BUILD.gn index a250fea66710..6637583705e6 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -1,3 +1,8 @@ +# 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/. + import("//brave/android/android_browser_tests.gni") import("//brave/browser/ethereum_remote_client/buildflags/buildflags.gni") import("//brave/browser/metrics/buildflags/buildflags.gni") From 95f1188765d363d529f2795461f08a39faa8fc51 Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Tue, 6 Dec 2022 13:03:34 -0800 Subject: [PATCH 6/7] Fix optional signedTransaction on Android --- .../org/chromium/chrome/browser/crypto_wallet/util/Utils.java | 1 + .../chrome/browser/brave_wallet/BraveWalletUtilsTest.java | 4 +++- 2 files changed, 4 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 41eaaffa54b8..b0dc9037ab49 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 @@ -637,6 +637,7 @@ public static TxData getTxData( res.to = to; res.value = value; res.data = data; + res.signedTransaction = ""; return res; } diff --git a/android/javatests/org/chromium/chrome/browser/brave_wallet/BraveWalletUtilsTest.java b/android/javatests/org/chromium/chrome/browser/brave_wallet/BraveWalletUtilsTest.java index 6308d1c1e567..df95f1e73152 100644 --- a/android/javatests/org/chromium/chrome/browser/brave_wallet/BraveWalletUtilsTest.java +++ b/android/javatests/org/chromium/chrome/browser/brave_wallet/BraveWalletUtilsTest.java @@ -402,7 +402,8 @@ public void validateTxDataTest() { String varName = f.getName(); if (varName.equals("nonce") || varName.equals("gasPrice") || varName.equals("gasLimit") || varName.equals("to") - || varName.equals("value") || varName.equals("data")) { + || varName.equals("value") || varName.equals("data") + || varName.equals("signedTransaction")) { continue; } if (v == null) { @@ -424,6 +425,7 @@ public void validateTxDataTest() { testStruct.to = ""; testStruct.value = ""; testStruct.data = new byte[0]; + testStruct.signedTransaction = ""; try { java.nio.ByteBuffer byteBuffer = testStruct.serialize(); TxData testStructDeserialized = TxData.deserialize(byteBuffer); From 6e3b5442e6f6f1f5679ab9d4d078a167222cbdae Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Wed, 7 Dec 2022 12:18:25 -0800 Subject: [PATCH 7/7] review feedback --- components/brave_wallet_ui/components/shared/style.tsx | 3 ++- components/resources/wallet_strings.grdp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/brave_wallet_ui/components/shared/style.tsx b/components/brave_wallet_ui/components/shared/style.tsx index 64aa32c2a2a7..898c5f77740c 100644 --- a/components/brave_wallet_ui/components/shared/style.tsx +++ b/components/brave_wallet_ui/components/shared/style.tsx @@ -145,7 +145,8 @@ export const StatusBubble = styled.div<{ status: BraveWallet.TransactionStatus } border-radius: 100%; opacity: ${(p) => p.status === BraveWallet.TransactionStatus.Submitted || p.status === BraveWallet.TransactionStatus.Approved || - p.status === BraveWallet.TransactionStatus.Unapproved + p.status === BraveWallet.TransactionStatus.Unapproved || + p.status === BraveWallet.TransactionStatus.Signed ? 0.4 : 1 }; diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp index ca32d74d195b..1341dd3ba449 100644 --- a/components/resources/wallet_strings.grdp +++ b/components/resources/wallet_strings.grdp @@ -512,7 +512,7 @@ Transaction submitted Transaction has been successfully sent to the network and awaits confirmation. Transaction signed - Transaction has been signed and will be sent to network by dapps and awaits confirmation + The transaction has been signed and will be sent to network by the DApp $11 ETH was returned to your wallet Transaction failed Transaction was failed due to a large price movement. Increase slippage tolerance to succeed at a larger price movement.