Skip to content

feat(wallet): Advanced Solana NFT gallery support (frontend desktop changes) #16276

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

Closed
wants to merge 17 commits into from
Closed
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
2 changes: 2 additions & 0 deletions components/brave_wallet/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ static_library("browser") {
"json_rpc_service.h",
"keyring_service.cc",
"keyring_service.h",
"nft_metadata_fetcher.cc",
"nft_metadata_fetcher.h",
"nonce_tracker.cc",
"nonce_tracker.h",
"password_encryptor.cc",
Expand Down
181 changes: 35 additions & 146 deletions components/brave_wallet/browser/json_rpc_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
#include "brave/components/brave_wallet/common/web3_provider_constants.h"
#include "brave/components/decentralized_dns/core/constants.h"
#include "brave/components/decentralized_dns/core/utils.h"
#include "brave/components/ipfs/buildflags/buildflags.h"
#include "components/grit/brave_components_strings.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
Expand All @@ -57,11 +56,6 @@
#include "ui/base/l10n/l10n_util.h"
#include "url/origin.h"

#if BUILDFLAG(ENABLE_IPFS)
#include "brave/components/ipfs/ipfs_constants.h"
#include "brave/components/ipfs/ipfs_utils.h"
#endif

using api_request_helper::APIRequestHelper;
using decentralized_dns::EnsOffchainResolveMethod;

Expand Down Expand Up @@ -182,6 +176,9 @@ JsonRpcService::JsonRpcService(
api_request_helper_ens_offchain_ = std::make_unique<APIRequestHelper>(
GetENSOffchainNetworkTrafficAnnotationTag(), url_loader_factory);
}

nft_metadata_fetcher_ = std::unique_ptr<NftMetadataFetcher>(
new NftMetadataFetcher(url_loader_factory, this, prefs_));
}

JsonRpcService::JsonRpcService(
Expand Down Expand Up @@ -1961,45 +1958,45 @@ void JsonRpcService::ContinueGetERC721TokenBalance(
void JsonRpcService::GetERC721Metadata(const std::string& contract_address,
const std::string& token_id,
const std::string& chain_id,
GetTokenMetadataCallback callback) {
JsonRpcService::GetTokenMetadata(contract_address, token_id, chain_id,
kERC721MetadataInterfaceId,
std::move(callback));
GetERC721MetadataCallback callback) {
nft_metadata_fetcher_->GetEthTokenMetadata(
contract_address, token_id, chain_id, kERC721MetadataInterfaceId,
std::move(callback));
}

void JsonRpcService::GetERC1155Metadata(const std::string& contract_address,
const std::string& token_id,
const std::string& chain_id,
GetTokenMetadataCallback callback) {
JsonRpcService::GetTokenMetadata(contract_address, token_id, chain_id,
kERC1155MetadataInterfaceId,
std::move(callback));
GetERC1155MetadataCallback callback) {
nft_metadata_fetcher_->GetEthTokenMetadata(
contract_address, token_id, chain_id, kERC1155MetadataInterfaceId,
std::move(callback));
}

void JsonRpcService::GetTokenMetadata(const std::string& contract_address,
const std::string& token_id,
const std::string& chain_id,
const std::string& interface_id,
GetTokenMetadataCallback callback) {
void JsonRpcService::GetEthTokenUri(const std::string& chain_id,
const std::string& contract_address,
const std::string& token_id,
const std::string& interface_id,
GetEthTokenUriCallback callback) {
auto network_url = GetNetworkURL(prefs_, chain_id, mojom::CoinType::ETH);
if (!network_url.is_valid()) {
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
GURL(), mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}

if (!EthAddress::IsValidAddress(contract_address)) {
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
GURL(), mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}

uint256_t token_id_uint = 0;
if (!HexValueToUint256(token_id, &token_id_uint)) {
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
GURL(), mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}
Expand All @@ -2008,68 +2005,39 @@ void JsonRpcService::GetTokenMetadata(const std::string& contract_address,
if (interface_id == kERC721MetadataInterfaceId) {
if (!erc721::TokenUri(token_id_uint, &function_signature)) {
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
GURL(), mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}
} else if (interface_id == kERC1155MetadataInterfaceId) {
if (!erc1155::Uri(token_id_uint, &function_signature)) {
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
GURL(), mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}
} else {
// Unknown inteface ID
std::move(callback).Run(
"", mojom::ProviderError::kInvalidParams,
GURL(), mojom::ProviderError::kInvalidParams,
l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
return;
}

auto internal_callback =
base::BindOnce(&JsonRpcService::OnGetSupportsInterfaceTokenMetadata,
weak_ptr_factory_.GetWeakPtr(), contract_address,
function_signature, network_url, std::move(callback));

GetSupportsInterface(contract_address, interface_id, chain_id,
std::move(internal_callback));
}

void JsonRpcService::OnGetSupportsInterfaceTokenMetadata(
const std::string& contract_address,
const std::string& function_signature,
const GURL& network_url,
GetTokenMetadataCallback callback,
bool is_supported,
mojom::ProviderError error,
const std::string& error_message) {
if (error != mojom::ProviderError::kSuccess) {
std::move(callback).Run("", error, error_message);
return;
}

if (!is_supported) {
std::move(callback).Run(
"", mojom::ProviderError::kMethodNotSupported,
l10n_util::GetStringUTF8(IDS_WALLET_METHOD_NOT_SUPPORTED_ERROR));
return;
}

auto internal_callback =
base::BindOnce(&JsonRpcService::OnGetTokenUri,
base::BindOnce(&JsonRpcService::OnGetEthTokenUri,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));

RequestInternal(eth::eth_call("", contract_address, "", "", "",
function_signature, kEthereumBlockTagLatest),
true, network_url, std::move(internal_callback));
}

void JsonRpcService::OnGetTokenUri(GetTokenMetadataCallback callback,
APIRequestResult api_request_result) {
void JsonRpcService::OnGetEthTokenUri(GetEthTokenUriCallback callback,
APIRequestResult api_request_result) {
if (!api_request_result.Is2XXResponseCode()) {
std::move(callback).Run(
"", mojom::ProviderError::kInternalError,
GURL(), mojom::ProviderError::kInternalError,
l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR));
return;
}
Expand All @@ -2081,97 +2049,11 @@ void JsonRpcService::OnGetTokenUri(GetTokenMetadataCallback callback,
std::string error_message;
ParseErrorResult<mojom::ProviderError>(api_request_result.body(), &error,
&error_message);
std::move(callback).Run("", error, error_message);
std::move(callback).Run(url, error, error_message);
return;
}

// Obtain JSON from the URL depending on the scheme.
// IPFS, HTTPS, and data URIs are supported.
// IPFS and HTTPS URIs require an additional request to fetch the metadata.
std::string metadata_json;
std::string scheme = url.scheme();
#if BUILDFLAG(ENABLE_IPFS)
if (scheme != url::kDataScheme && scheme != url::kHttpsScheme &&
scheme != ipfs::kIPFSScheme) {
#else
if (scheme != url::kDataScheme && scheme != url::kHttpsScheme) {
#endif
std::move(callback).Run(
"", mojom::ProviderError::kMethodNotSupported,
l10n_util::GetStringUTF8(IDS_WALLET_METHOD_NOT_SUPPORTED_ERROR));
return;
}

if (scheme == url::kDataScheme) {
if (!eth::ParseDataURIAndExtractJSON(url, &metadata_json)) {
std::move(callback).Run(
"", mojom::ProviderError::kParsingError,
l10n_util::GetStringUTF8(IDS_WALLET_PARSING_ERROR));
return;
}

// Sanitize JSON
data_decoder::JsonSanitizer::Sanitize(
std::move(metadata_json),
base::BindOnce(&JsonRpcService::OnSanitizeTokenMetadata,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
return;
}

#if BUILDFLAG(ENABLE_IPFS)
if (scheme == ipfs::kIPFSScheme &&
!ipfs::TranslateIPFSURI(url, &url, ipfs::GetDefaultNFTIPFSGateway(prefs_),
false)) {
std::move(callback).Run("", mojom::ProviderError::kParsingError,
l10n_util::GetStringUTF8(IDS_WALLET_PARSING_ERROR));
return;
}
#endif

auto internal_callback =
base::BindOnce(&JsonRpcService::OnGetTokenMetadataPayload,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
api_request_helper_->Request("GET", url, "", "", true,
std::move(internal_callback));
}

void JsonRpcService::OnSanitizeTokenMetadata(
GetTokenMetadataCallback callback,
data_decoder::JsonSanitizer::Result result) {
if (result.error) {
VLOG(1) << "Data URI JSON validation error:" << *result.error;
std::move(callback).Run("", mojom::ProviderError::kParsingError,
l10n_util::GetStringUTF8(IDS_WALLET_PARSING_ERROR));
return;
}

std::string metadata_json;
if (result.value.has_value()) {
metadata_json = result.value.value();
}

std::move(callback).Run(metadata_json, mojom::ProviderError::kSuccess, "");
}

void JsonRpcService::OnGetTokenMetadataPayload(
GetTokenMetadataCallback 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;
}

// Invalid JSON becomes an empty string after sanitization
if (api_request_result.body().empty()) {
std::move(callback).Run("", mojom::ProviderError::kParsingError,
l10n_util::GetStringUTF8(IDS_WALLET_PARSING_ERROR));
return;
}

std::move(callback).Run(api_request_result.body(),
mojom::ProviderError::kSuccess, "");
std::move(callback).Run(url, mojom::ProviderError::kSuccess, "");
}

void JsonRpcService::GetERC1155TokenBalance(
Expand Down Expand Up @@ -2527,6 +2409,13 @@ void JsonRpcService::OnGetSPLTokenAccountBalance(
std::move(callback).Run(amount, decimals, ui_amount_string,
mojom::SolanaProviderError::kSuccess, "");
}

void JsonRpcService::GetSolTokenMetadata(const std::string& token_mint_address,
GetSolTokenMetadataCallback callback) {
nft_metadata_fetcher_->GetSolTokenMetadata(token_mint_address,
std::move(callback));
}

void JsonRpcService::SendFilecoinTransaction(
const std::string& signed_tx,
SendFilecoinTransactionCallback callback) {
Expand Down
Loading