Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetEthTokenSymbol and GetEthTokenDecimals to JsonRpcService #18254

Merged
merged 4 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions components/brave_wallet/browser/json_rpc_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "brave/components/brave_wallet/common/eth_abi_utils.h"
#include "brave/components/brave_wallet/common/eth_address.h"
#include "brave/components/brave_wallet/common/features.h"
#include "brave/components/brave_wallet/common/hash_utils.h"
#include "brave/components/brave_wallet/common/hex_utils.h"
#include "brave/components/decentralized_dns/core/constants.h"
#include "brave/components/decentralized_dns/core/utils.h"
Expand Down Expand Up @@ -2652,6 +2653,93 @@ bool JsonRpcService::AddSwitchEthereumChainRequest(const std::string& chain_id,
return true;
}

void JsonRpcService::GetEthTokenSymbol(const std::string& contract_address,
const std::string& chain_id,
GetEthTokenSymbolCallback callback) {
auto network_url = GetNetworkURL(prefs_, chain_id, mojom::CoinType::ETH);
if (!network_url.is_valid()) {
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}
const std::string data = GetFunctionHash("symbol()");
auto internal_callback =
base::BindOnce(&JsonRpcService::OnGetEthTokenSymbol,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
RequestInternal(eth::eth_call(contract_address, data), true, network_url,
std::move(internal_callback));
}

void JsonRpcService::OnGetEthTokenSymbol(GetEthTokenSymbolCallback callback,
APIRequestResult api_request_result) {
if (!api_request_result.Is2XXResponseCode()) {
std::move(callback).Run(
"", mojom::ProviderError::kInternalError,
l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR));
return;
}

std::string symbol;
if (!eth::ParseStringResult(api_request_result.value_body(), &symbol)) {
mojom::ProviderError error;
std::string error_message;
ParseErrorResult<mojom::ProviderError>(api_request_result.value_body(),
&error, &error_message);
std::move(callback).Run("", error, error_message);
return;
}
std::move(callback).Run(symbol, mojom::ProviderError::kSuccess, "");
}

void JsonRpcService::GetEthTokenDecimals(const std::string& contract_address,
const std::string& chain_id,
GetEthTokenDecimalsCallback callback) {
auto network_url = GetNetworkURL(prefs_, chain_id, mojom::CoinType::ETH);
if (!network_url.is_valid()) {
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}
const std::string data = GetFunctionHash("decimals()");
auto internal_callback =
base::BindOnce(&JsonRpcService::OnGetEthTokenDecimals,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
RequestInternal(eth::eth_call(contract_address, data), true, network_url,
std::move(internal_callback));
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note - Jocelyn had asked that GetEthTokenSymbol and GetEthTokenDecimals share the code since they are similar, however because symbol()and decimals() return different types (string and uint8 respectively), their parsing logic is different, so I didn't see much opportunity for reuse.


void JsonRpcService::OnGetEthTokenDecimals(
GetEthTokenDecimalsCallback callback,
APIRequestResult api_request_result) {
if (!api_request_result.Is2XXResponseCode()) {
std::move(callback).Run(
"", mojom::ProviderError::kInternalError,
l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR));
return;
}

auto result = eth::ParseEthCall(api_request_result.value_body());
if (!result) {
mojom::ProviderError error;
std::string error_message;
ParseErrorResult<mojom::ProviderError>(api_request_result.value_body(),
&error, &error_message);
std::move(callback).Run("", error, error_message);
return;
}

const auto& args = eth::DecodeEthCallResponse(*result, {"uint8"});
if (args == absl::nullopt) {
std::move(callback).Run("", mojom::ProviderError::kParsingError,
l10n_util::GetStringUTF8(IDS_WALLET_PARSING_ERROR));
return;
}

std::move(callback).Run(args->at(0), mojom::ProviderError::kSuccess, "");
}

void JsonRpcService::Reset() {
ClearJsonRpcServiceProfilePrefs(prefs_);

Expand Down
13 changes: 13 additions & 0 deletions components/brave_wallet/browser/json_rpc_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,14 @@ class JsonRpcService : public KeyedService, public mojom::JsonRpcService {
mojom::ProviderError error,
const std::string& error_message);

void GetEthTokenSymbol(const std::string& contract_address,
const std::string& chain_id,
GetEthTokenSymbolCallback callback) override;

void GetEthTokenDecimals(const std::string& contract_address,
const std::string& chain_id,
GetEthTokenDecimalsCallback callback) override;

using SwitchEthereumChainRequestCallback =
base::OnceCallback<void(mojom::ProviderError error,
const std::string& error_message)>;
Expand Down Expand Up @@ -631,6 +639,11 @@ class JsonRpcService : public KeyedService, public mojom::JsonRpcService {
void OnGetSupportsInterface(GetSupportsInterfaceCallback callback,
APIRequestResult api_request_result);

void OnGetEthTokenSymbol(GetEthTokenSymbolCallback callback,
APIRequestResult api_request_result);

void OnGetEthTokenDecimals(GetEthTokenDecimalsCallback callback,
APIRequestResult api_request_result);
// Solana
void OnGetSolanaBalance(GetSolanaBalanceCallback callback,
APIRequestResult api_request_result);
Expand Down
125 changes: 125 additions & 0 deletions components/brave_wallet/browser/json_rpc_service_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,17 @@ class JsonRpcEnpointHandler {
std::vector<SolRpcCallHandler*> sol_rpc_call_handlers_;
};

constexpr char kJsonRpcResponseTemplate[] = R"({
"jsonrpc":"2.0",
"id":1,
"result":"$1"
})";

std::string FormatJsonRpcResponse(const std::string& value) {
return base::ReplaceStringPlaceholders(kJsonRpcResponseTemplate, {value},
nullptr);
}

} // namespace

class JsonRpcServiceUnitTest : public testing::Test {
Expand Down Expand Up @@ -1270,6 +1281,44 @@ class JsonRpcServiceUnitTest : public testing::Test {
run_loop.Run();
}

void TestGetEthTokenSymbol(const std::string& contract_address,
const std::string& chain_id,
const std::string& expected_symbol,
mojom::ProviderError expected_error,
const std::string& expected_error_message) {
base::RunLoop run_loop;
json_rpc_service_->GetEthTokenSymbol(
contract_address, chain_id,
base::BindLambdaForTesting([&](const std::string& symbol,
mojom::ProviderError error,
const std::string& error_message) {
EXPECT_EQ(symbol, expected_symbol);
EXPECT_EQ(error, expected_error);
EXPECT_EQ(error_message, expected_error_message);
run_loop.Quit();
}));
run_loop.Run();
}

void TestGetEthTokenDecimals(const std::string& contract_address,
const std::string& chain_id,
const std::string& expected_decimals,
mojom::ProviderError expected_error,
const std::string& expected_error_message) {
base::RunLoop run_loop;
json_rpc_service_->GetEthTokenDecimals(
contract_address, chain_id,
base::BindLambdaForTesting([&](const std::string& decimals,
mojom::ProviderError error,
const std::string& error_message) {
EXPECT_EQ(decimals, expected_decimals);
EXPECT_EQ(error, expected_error);
EXPECT_EQ(error_message, expected_error_message);
run_loop.Quit();
}));
run_loop.Run();
}

void TestGetSolanaBalance(uint64_t expected_balance,
mojom::SolanaProviderError expected_error,
const std::string& expected_error_message) {
Expand Down Expand Up @@ -6584,4 +6633,80 @@ TEST_F(JsonRpcServiceUnitTest, GetEthNftStandard) {
mojom::ProviderError::kSuccess, "");
}

TEST_F(JsonRpcServiceUnitTest, GetEthTokenSymbol) {
// Invalid chain ID yields invalid params
TestGetEthTokenSymbol(
"0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "", "",
mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));

// Valid inputs but request times out yields internal error
SetHTTPRequestTimeoutInterceptor();
TestGetEthTokenSymbol("0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
mojom::kMainnetChainId, "",
mojom::ProviderError::kInternalError,
l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR));

// Valid
const std::string bat_symbol_result =
"0x"
"0000000000000000000000000000000000000000000000000000000000000020"
"0000000000000000000000000000000000000000000000000000000000000003"
"4241540000000000000000000000000000000000000000000000000000000000";
SetInterceptor(GetNetwork(mojom::kMainnetChainId, mojom::CoinType::ETH),
"eth_call", "", FormatJsonRpcResponse(bat_symbol_result));
TestGetEthTokenSymbol("0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
mojom::kMainnetChainId, "BAT",
mojom::ProviderError::kSuccess, "");

// Response parsing error yields parsing error
SetInterceptor(GetNetwork(mojom::kMainnetChainId, mojom::CoinType::ETH),
"eth_call", "", R"({
"jsonrpc": "2.0",
"id": 1,
"result": "0xinvalid"
})");
TestGetEthTokenSymbol("0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
mojom::kMainnetChainId, "",
mojom::ProviderError::kParsingError,
l10n_util::GetStringUTF8(IDS_WALLET_PARSING_ERROR));
}

TEST_F(JsonRpcServiceUnitTest, GetEthTokenDecimals) {
// Invalid chain ID yields invalid params
TestGetEthTokenDecimals(
"0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "", "",
mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));

// Valid inputs but request times out yields internal error
SetHTTPRequestTimeoutInterceptor();
TestGetEthTokenDecimals("0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
mojom::kMainnetChainId, "",
mojom::ProviderError::kInternalError,
l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR));

// Valid
const std::string bat_decimals_result =
"0x"
"0000000000000000000000000000000000000000000000000000000000000012";
SetInterceptor(GetNetwork(mojom::kMainnetChainId, mojom::CoinType::ETH),
"eth_call", "", FormatJsonRpcResponse(bat_decimals_result));
TestGetEthTokenDecimals("0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
mojom::kMainnetChainId, "0x12",
mojom::ProviderError::kSuccess, "");

// Response parsing error yields parsing error
SetInterceptor(GetNetwork(mojom::kMainnetChainId, mojom::CoinType::ETH),
"eth_call", "", R"({
"jsonrpc": "2.0",
"id": 1,
"result": "0xinvalid"
})");
TestGetEthTokenDecimals("0x0D8775F648430679A709E98d2b0Cb6250d2887EF",
mojom::kMainnetChainId, "",
mojom::ProviderError::kParsingError,
l10n_util::GetStringUTF8(IDS_WALLET_PARSING_ERROR));
}

} // namespace brave_wallet
11 changes: 11 additions & 0 deletions components/brave_wallet/common/brave_wallet.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,17 @@ interface JsonRpcService {
// Obtains the quantity of ERC1155 tokens a user has
GetERC1155TokenBalance(string contract_address, string token_id, string account_address, string chain_id) => (string balance, ProviderError error, string error_message);

// Fetches symbol of an ERC20 or ERC721 token.
GetEthTokenSymbol(string chain_id,
string contract_address) => (string symbol,
ProviderError error,
string error_message);

// Fetches decimals of an ERC20 or ERC721 token.
GetEthTokenDecimals(string chain_id,
string contract_address) => (string decimals,
ProviderError error,
string error_message);
// Solana JSON RPCs
// https://docs.solana.com/developing/clients/jsonrpc-api

Expand Down