Skip to content

Commit

Permalink
Add GetEthTokenSymbol and GetEthTokenDecimals to JsonRpcService (#18254)
Browse files Browse the repository at this point in the history
* Add GetEthTokenSymbol to JsonRpcService

* Add GetEthTokenDecimals to JsonRpcService
  • Loading branch information
nvonpentz authored Apr 28, 2023
1 parent eeca4b4 commit 117877c
Show file tree
Hide file tree
Showing 4 changed files with 237 additions and 0 deletions.
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));
}

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

0 comments on commit 117877c

Please sign in to comment.