+ menu-options="[[resolveMethod_]]">
$i18n{resolveENSDesc}
+ menu-options="[[resolveMethod_]]">
@@ -110,6 +110,14 @@
pref="{{prefs.brave.ens.offchain_resolve_method}}" menu-options="[[ensOffchainResolveMethod_]]">
+
+
+
$i18n{resolveSnsDesc}
+
+
+
{
this.showRestartToast_ = needsRestart
})
@@ -76,10 +79,7 @@ export class SettingBraveDefaultExtensionsPageElement extends SettingBraveDefaul
this.showRestartToast_ = show
})
this.browserProxy_.getDecentralizedDnsResolveMethodList().then(list => {
- this.unstoppableDomainsResolveMethod_ = list
- })
- this.browserProxy_.getDecentralizedDnsResolveMethodList().then(list => {
- this.ensResolveMethod_ = list
+ this.resolveMethod_ = list
})
this.browserProxy_.getEnsOffchainResolveMethodList().then(list => {
this.ensOffchainResolveMethod_ = list
diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd
index 325601b93e23..f1f31cadf8f0 100644
--- a/browser/ui/android/strings/android_brave_strings.grd
+++ b/browser/ui/android/strings/android_brave_strings.grd
@@ -332,8 +332,8 @@ This file contains all "about" strings. It is set to NOT be translated, in tran
Disabled
-
- Ethereum
+
+ Enabled
Today’s top stories in a completely private feed, just for you.
diff --git a/browser/ui/webui/settings/brave_default_extensions_handler.cc b/browser/ui/webui/settings/brave_default_extensions_handler.cc
index 21c31b17256f..138261ed8138 100644
--- a/browser/ui/webui/settings/brave_default_extensions_handler.cc
+++ b/browser/ui/webui/settings/brave_default_extensions_handler.cc
@@ -86,9 +86,9 @@ base::Value::List GetResolveMethodList() {
brave_l10n::GetLocalizedResourceUTF16String(
IDS_DECENTRALIZED_DNS_RESOLVE_OPTION_DISABLED)));
list.Append(
- MakeSelectValue(ResolveMethodTypes::ETHEREUM,
+ MakeSelectValue(ResolveMethodTypes::ENABLED,
brave_l10n::GetLocalizedResourceUTF16String(
- IDS_DECENTRALIZED_DNS_RESOLVE_OPTION_ETHEREUM)));
+ IDS_DECENTRALIZED_DNS_RESOLVE_OPTION_ENABLED)));
return list;
}
diff --git a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc
index ce017aa6c6ac..38fd18522106 100644
--- a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc
+++ b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc
@@ -363,6 +363,7 @@ void BraveAddCommonStrings(content::WebUIDataSource* html_source,
IDS_SETTINGS_RESOLVE_UNSTOPPABLE_DOMAINS_DESC},
{"resolveENSDesc", IDS_SETTINGS_RESOLVE_ENS_DESC},
{"ensOffchainLookupTitle", IDS_SETTINGS_ENABLE_ENS_OFFCHAIN_LOOKUP_TITLE},
+ {"resolveSnsDesc", IDS_SETTINGS_RESOLVE_SNS_DESC},
{"resolveIPFSURLDesc", IDS_SETTINGS_RESOLVE_IPFS_URLS_DESC},
{"ipfsPublicGatewayDesc", IDS_SETTINGS_IPFS_PUBLIC_GATEWAY_DESC},
{"ipfsNftPublicGatewayDesc", IDS_SETTINGS_IPFS_PUBLIC_NFT_GATEWAY_DESC},
@@ -679,6 +680,10 @@ void BraveAddLocalizedStrings(content::WebUIDataSource* html_source,
"isENSL2Enabled", base::FeatureList::IsEnabled(
brave_wallet::features::kBraveWalletENSL2Feature));
+ html_source->AddBoolean("isSnsEnabled",
+ base::FeatureList::IsEnabled(
+ brave_wallet::features::kBraveWalletSnsFeature));
+
if (base::FeatureList::IsEnabled(
net::features::kBraveFirstPartyEphemeralStorage)) {
const webui::LocalizedString kSessionOnlyToEphemeralStrings[] = {
diff --git a/chromium_src/net/base/lookup_string_in_fixed_set.cc b/chromium_src/net/base/lookup_string_in_fixed_set.cc
index bc98e965213f..87c3c175c95c 100644
--- a/chromium_src/net/base/lookup_string_in_fixed_set.cc
+++ b/chromium_src/net/base/lookup_string_in_fixed_set.cc
@@ -56,6 +56,10 @@ int LookupSuffixInReversedSet(const unsigned char* graph,
*suffix_length = strlen(decentralized_dns::kEthDomain) - 1;
return kDafsaFound;
}
+ if (base::EndsWith(host, decentralized_dns::kSolDomain)) {
+ *suffix_length = strlen(decentralized_dns::kSolDomain) - 1;
+ return kDafsaFound;
+ }
if (include_private &&
base::EndsWith(host, decentralized_dns::kDNSForEthDomain)) {
diff --git a/components/brave_wallet/browser/json_rpc_service.cc b/components/brave_wallet/browser/json_rpc_service.cc
index d665a9786107..f1e8b6516ab2 100644
--- a/components/brave_wallet/browser/json_rpc_service.cc
+++ b/components/brave_wallet/browser/json_rpc_service.cc
@@ -1393,7 +1393,7 @@ void JsonRpcService::SnsGetSolAddr(const std::string& domain,
sns_get_sol_addr_tasks_.AddTask(
std::make_unique(std::move(done_callback),
api_request_helper_.get(), domain,
- network_url),
+ network_url, true),
std::move(callback));
}
@@ -1425,6 +1425,75 @@ void JsonRpcService::OnSnsGetSolAddrTaskDone(
}
}
+void JsonRpcService::SnsResolveHost(const std::string& domain,
+ SnsResolveHostCallback callback) {
+ if (!base::FeatureList::IsEnabled(features::kBraveWalletSnsFeature)) {
+ std::move(callback).Run(
+ GURL(), mojom::SolanaProviderError::kInvalidParams,
+ l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
+ return;
+ }
+
+ if (!IsValidDomain(domain)) {
+ std::move(callback).Run(
+ GURL(), mojom::SolanaProviderError::kInvalidParams,
+ l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
+ return;
+ }
+
+ if (sns_resolve_host_tasks_.ContainsTaskForDomain(domain)) {
+ sns_resolve_host_tasks_.AddCallbackForDomain(domain, std::move(callback));
+ return;
+ }
+
+ GURL network_url = GetNetworkURL(prefs_, brave_wallet::mojom::kSolanaMainnet,
+ mojom::CoinType::SOL);
+ if (!network_url.is_valid()) {
+ std::move(callback).Run(
+ GURL(), mojom::SolanaProviderError::kInvalidParams,
+ l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS));
+ return;
+ }
+
+ // JsonRpcService owns EnsResolverTask instance, so Unretained is safe here.
+ auto done_callback = base::BindOnce(&JsonRpcService::OnSnsResolveHostTaskDone,
+ base::Unretained(this));
+
+ sns_resolve_host_tasks_.AddTask(
+ std::make_unique(std::move(done_callback),
+ api_request_helper_.get(), domain,
+ network_url, false),
+ std::move(callback));
+}
+
+void JsonRpcService::OnSnsResolveHostTaskDone(
+ SnsResolverTask* task,
+ absl::optional task_result,
+ absl::optional task_error) {
+ auto callbacks = sns_resolve_host_tasks_.TaskDone(task);
+ if (callbacks.empty()) {
+ return;
+ }
+
+ GURL url;
+ mojom::SolanaProviderError error =
+ task_error ? task_error->error : mojom::SolanaProviderError::kSuccess;
+ std::string error_message = task_error ? task_error->error_message : "";
+
+ if (task_result) {
+ if (task_result->resolved_url.is_valid()) {
+ url = task_result->resolved_url;
+ } else {
+ error = mojom::SolanaProviderError::kInvalidParams;
+ error_message = l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS);
+ }
+ }
+
+ for (auto& cb : callbacks) {
+ std::move(cb).Run(url, error, error_message);
+ }
+}
+
void JsonRpcService::OnEnsGetContentHashTaskDone(
EnsResolverTask* task,
absl::optional task_result,
diff --git a/components/brave_wallet/browser/json_rpc_service.h b/components/brave_wallet/browser/json_rpc_service.h
index 5204a3f1d0f2..ecfae3a96144 100644
--- a/components/brave_wallet/browser/json_rpc_service.h
+++ b/components/brave_wallet/browser/json_rpc_service.h
@@ -180,6 +180,13 @@ class JsonRpcService : public KeyedService, public mojom::JsonRpcService {
void SnsGetSolAddr(const std::string& domain,
SnsGetSolAddrCallback callback) override;
+ using SnsResolveHostCallback =
+ base::OnceCallback;
+ void SnsResolveHost(const std::string& domain,
+ SnsResolveHostCallback callback);
+
bool SetNetwork(const std::string& chain_id,
mojom::CoinType coin,
bool silent = false);
@@ -481,6 +488,10 @@ class JsonRpcService : public KeyedService, public mojom::JsonRpcService {
SnsResolverTask* task,
absl::optional task_result,
absl::optional error);
+ void OnSnsResolveHostTaskDone(
+ SnsResolverTask* task,
+ absl::optional task_result,
+ absl::optional error);
void OnEnsGetEthAddr(EnsGetEthAddrCallback callback,
APIRequestResult api_request_result);
void OnGetFilEstimateGas(GetFilEstimateGasCallback callback,
@@ -592,6 +603,7 @@ class JsonRpcService : public KeyedService, public mojom::JsonRpcService {
ens_get_content_hash_tasks_;
SnsResolverTaskContainer sns_get_sol_addr_tasks_;
+ SnsResolverTaskContainer sns_resolve_host_tasks_;
mojo::ReceiverSet receivers_;
PrefService* prefs_ = nullptr;
diff --git a/components/brave_wallet/browser/json_rpc_service_unittest.cc b/components/brave_wallet/browser/json_rpc_service_unittest.cc
index 810c5a82e991..73df7f295619 100644
--- a/components/brave_wallet/browser/json_rpc_service_unittest.cc
+++ b/components/brave_wallet/browser/json_rpc_service_unittest.cc
@@ -471,6 +471,11 @@ class GetAccountInfoHandler : public SolRpcCallHandler {
return result;
}
+ static std::vector MakeTextRecordPayloadData(
+ const std::string& text) {
+ return std::vector(text.begin(), text.end());
+ }
+
absl::optional HandleCall(
const base::Value::Dict& dict) override {
if (fail_with_timeout_)
@@ -5705,6 +5710,20 @@ class SnsJsonRpcServiceUnitTest : public JsonRpcServiceUnitTest {
SolRecordAddress(), GetRecordKeyAddress("SOL"),
domain_owner_private_key_)));
+ url_record_address_handler_ = std::make_unique(
+ GetRecordKeyAddress("url"), SolanaAddress::ZeroAddress(),
+ GetAccountInfoHandler::MakeNameRegistryStateData(
+ DomainOwnerAddress(),
+ GetAccountInfoHandler::MakeTextRecordPayloadData(
+ url_value().spec())));
+
+ ipfs_record_address_handler_ = std::make_unique(
+ GetRecordKeyAddress("IPFS"), SolanaAddress::ZeroAddress(),
+ GetAccountInfoHandler::MakeNameRegistryStateData(
+ DomainOwnerAddress(),
+ GetAccountInfoHandler::MakeTextRecordPayloadData(
+ ipfs_value().spec())));
+
default_handler_ = std::make_unique();
json_rpc_endpoint_handler_->AddSolRpcCallHandler(
@@ -5717,6 +5736,11 @@ class SnsJsonRpcServiceUnitTest : public JsonRpcServiceUnitTest {
json_rpc_endpoint_handler_->AddSolRpcCallHandler(
sol_record_address_handler_.get());
+ json_rpc_endpoint_handler_->AddSolRpcCallHandler(
+ url_record_address_handler_.get());
+ json_rpc_endpoint_handler_->AddSolRpcCallHandler(
+ ipfs_record_address_handler_.get());
+
json_rpc_endpoint_handler_->AddSolRpcCallHandler(default_handler_.get());
url_loader_factory_.SetInterceptor(base::BindRepeating(
@@ -5754,6 +5778,12 @@ class SnsJsonRpcServiceUnitTest : public JsonRpcServiceUnitTest {
"RecPwner11111111111111111111111111111111111");
}
+ GURL url_value() const { return GURL("https://brave.com"); }
+ GURL ipfs_value() const {
+ return GURL(
+ "ipfs://bafybeibd4ala53bs26dvygofvr6ahpa7gbw4eyaibvrbivf4l5rr44yqu4");
+ }
+
std::string sns_host() const { return "sub.test.sol"; }
protected:
@@ -5777,6 +5807,8 @@ class SnsJsonRpcServiceUnitTest : public JsonRpcServiceUnitTest {
std::unique_ptr get_program_accounts_handler_;
std::unique_ptr domain_address_handler_;
std::unique_ptr sol_record_address_handler_;
+ std::unique_ptr url_record_address_handler_;
+ std::unique_ptr ipfs_record_address_handler_;
std::unique_ptr default_handler_;
std::unique_ptr json_rpc_endpoint_handler_;
@@ -5896,4 +5928,54 @@ TEST_F(SnsJsonRpcServiceUnitTest, GetWalletAddr_SolRecordOwner) {
testing::Mock::VerifyAndClearExpectations(&callback);
}
+TEST_F(SnsJsonRpcServiceUnitTest, ResolveHost_UrlValue) {
+ base::MockCallback callback;
+ EXPECT_CALL(callback,
+ Run(url_value(), mojom::SolanaProviderError::kSuccess, ""));
+ json_rpc_service_->SnsResolveHost(sns_host(), callback.Get());
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(&callback);
+
+ // HTTP error for url record account. Fail resolution.
+ url_record_address_handler_->FailWithTimeout();
+ EXPECT_CALL(callback,
+ Run(GURL(), mojom::SolanaProviderError::kInternalError,
+ l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)));
+ json_rpc_service_->SnsResolveHost(sns_host(), callback.Get());
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(&callback);
+ url_record_address_handler_->FailWithTimeout(false);
+}
+
+TEST_F(SnsJsonRpcServiceUnitTest, ResolveHost_IpfsValue) {
+ url_record_address_handler_->Disable();
+
+ // No url record. Will return ipfs record.
+ base::MockCallback callback;
+ EXPECT_CALL(callback,
+ Run(ipfs_value(), mojom::SolanaProviderError::kSuccess, ""));
+ json_rpc_service_->SnsResolveHost(sns_host(), callback.Get());
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(&callback);
+
+ // HTTP error for ipfs record account. Fail resolution.
+ ipfs_record_address_handler_->FailWithTimeout();
+ EXPECT_CALL(callback,
+ Run(GURL(), mojom::SolanaProviderError::kInternalError,
+ l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)));
+ json_rpc_service_->SnsResolveHost(sns_host(), callback.Get());
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(&callback);
+ ipfs_record_address_handler_->FailWithTimeout(false);
+
+ // No ipfs record account. Fail resolution.
+ ipfs_record_address_handler_->Disable();
+ EXPECT_CALL(callback,
+ Run(GURL(), mojom::SolanaProviderError::kInvalidParams,
+ l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS)));
+ json_rpc_service_->SnsResolveHost(sns_host(), callback.Get());
+ base::RunLoop().RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(&callback);
+}
+
} // namespace brave_wallet
diff --git a/components/brave_wallet/browser/sns_resolver_task.cc b/components/brave_wallet/browser/sns_resolver_task.cc
index d73d409bebd7..596d3aaa3b3f 100644
--- a/components/brave_wallet/browser/sns_resolver_task.cc
+++ b/components/brave_wallet/browser/sns_resolver_task.cc
@@ -376,11 +376,13 @@ class ScopedWorkOnSnsTask {
SnsResolverTask::SnsResolverTask(DoneCallback done_callback,
APIRequestHelper* api_request_helper,
const std::string& domain,
- const GURL& network_url)
+ const GURL& network_url,
+ bool resolve_address)
: done_callback_(std::move(done_callback)),
api_request_helper_(api_request_helper),
domain_(domain),
- network_url_(network_url) {
+ network_url_(network_url),
+ resolve_address_(resolve_address) {
DCHECK(api_request_helper_);
}
@@ -433,6 +435,14 @@ void SnsResolverTask::WorkOnTask() {
}
}
+ if (resolve_address_) {
+ WorkOnWalletAddressTask();
+ } else {
+ WorkOnDomainResolveTask();
+ }
+}
+
+void SnsResolverTask::WorkOnWalletAddressTask() {
// Check if there is an nft token associated with domain.
if (!nft_owner_check_done_) {
if (!nft_mint_address_) {
@@ -462,10 +472,26 @@ void SnsResolverTask::WorkOnTask() {
return;
}
- if (!sol_record_name_registry_state_) {
- FetchSolRecordRegistryState();
+ FetchSolRecordRegistryState();
+}
+
+void SnsResolverTask::WorkOnDomainResolveTask() {
+ if (!url_record_check_done_) {
+ FetchUrlRecordRegistryState();
return;
}
+
+ FetchIpfsRecordRegistryState();
+}
+
+void SnsResolverTask::SetAddressResult(SolanaAddress address) {
+ task_result_.emplace();
+ task_result_->resolved_address = std::move(address);
+}
+
+void SnsResolverTask::SetUrlResult(GURL url) {
+ task_result_.emplace();
+ task_result_->resolved_url = std::move(url);
}
void SnsResolverTask::SetError(SnsResolverTaskError error) {
@@ -588,8 +614,6 @@ void SnsResolverTask::OnFetchDomainRegistryState(
}
void SnsResolverTask::FetchSolRecordRegistryState() {
- DCHECK(!sol_record_name_registry_state_);
-
auto sol_record_address = GetDomainKey("SOL." + domain_, true);
if (!sol_record_address) {
// Put the domain owner as a fallback result and use it if there is no SOL
@@ -626,25 +650,143 @@ void SnsResolverTask::OnFetchSolRecordRegistryState(
if (!account_info) {
// No such account, use owner address.
- task_result_.emplace(domain_name_registry_state_->owner);
+ SetAddressResult(domain_name_registry_state_->owner);
return;
}
- sol_record_name_registry_state_ =
+ auto sol_record_name_registry_state =
FromBase64(account_info->data);
- if (!sol_record_name_registry_state_) {
+ if (!sol_record_name_registry_state) {
SetError(MakeInternalError());
return;
}
if (auto sol_record_payload_address = ParseAndVerifySolRecordData(
- base::make_span(sol_record_name_registry_state_->data),
+ base::make_span(sol_record_name_registry_state->data),
sol_record_address_, domain_name_registry_state_->owner)) {
- task_result_.emplace(*sol_record_payload_address);
+ SetAddressResult(*sol_record_payload_address);
} else {
// No valid address for SOL record, use owner address.
- task_result_.emplace(domain_name_registry_state_->owner);
+ SetAddressResult(domain_name_registry_state_->owner);
+ }
+}
+
+void SnsResolverTask::FetchUrlRecordRegistryState() {
+ auto url_record_address = GetDomainKey("url." + domain_, true);
+ if (!url_record_address) {
+ SetError(MakeInternalError());
+ ScheduleWorkOnTask();
+ return;
+ }
+
+ auto internal_callback =
+ base::BindOnce(&SnsResolverTask::OnFetchUrlRecordRegistryState,
+ weak_ptr_factory_.GetWeakPtr());
+ RequestInternal(solana::getAccountInfo(url_record_address->ToBase58()),
+ std::move(internal_callback),
+ solana::ConverterForGetAccountInfo());
+}
+
+void SnsResolverTask::OnFetchUrlRecordRegistryState(
+ APIRequestResult api_request_result) {
+ ScopedWorkOnSnsTask work_on_task(this);
+
+ if (!api_request_result.Is2XXResponseCode()) {
+ SetError(MakeInternalError());
+ return;
+ }
+
+ absl::optional account_info;
+ if (!solana::ParseGetAccountInfo(api_request_result.body(), &account_info)) {
+ SetError(MakeInternalError());
+ return;
+ }
+
+ if (!account_info) {
+ // No url record account, will proceed with ipfs record.
+ url_record_check_done_ = true;
+ return;
+ }
+
+ auto url_record_name_registry_state =
+ FromBase64(account_info->data);
+ if (!url_record_name_registry_state) {
+ SetError(MakeInternalError());
+ return;
+ }
+
+ // Trim possible zeros.
+ std::string url_string(
+ std::string(url_record_name_registry_state->data.begin(),
+ url_record_name_registry_state->data.end())
+ .c_str());
+
+ GURL url(url_string);
+ if (!url.is_valid()) {
+ SetError(MakeInvalidParamsError());
+ return;
+ }
+
+ SetUrlResult(std::move(url));
+}
+
+void SnsResolverTask::FetchIpfsRecordRegistryState() {
+ auto ipfs_record_address = GetDomainKey("IPFS." + domain_, true);
+ if (!ipfs_record_address) {
+ SetError(MakeInternalError());
+ ScheduleWorkOnTask();
+ return;
}
+
+ auto internal_callback =
+ base::BindOnce(&SnsResolverTask::OnFetchIpfsRecordRegistryState,
+ weak_ptr_factory_.GetWeakPtr());
+ RequestInternal(solana::getAccountInfo(ipfs_record_address->ToBase58()),
+ std::move(internal_callback),
+ solana::ConverterForGetAccountInfo());
+}
+
+void SnsResolverTask::OnFetchIpfsRecordRegistryState(
+ APIRequestResult api_request_result) {
+ ScopedWorkOnSnsTask work_on_task(this);
+
+ if (!api_request_result.Is2XXResponseCode()) {
+ SetError(MakeInternalError());
+ return;
+ }
+
+ absl::optional account_info;
+ if (!solana::ParseGetAccountInfo(api_request_result.body(), &account_info)) {
+ SetError(MakeInternalError());
+ return;
+ }
+
+ if (!account_info) {
+ // No ipfs record account, resolve as error.
+ SetError(MakeInvalidParamsError());
+ return;
+ }
+
+ auto ipfs_record_name_registry_state =
+ FromBase64(account_info->data);
+ if (!ipfs_record_name_registry_state) {
+ SetError(MakeInternalError());
+ return;
+ }
+
+ // Trim possible zeros.
+ std::string url_string(
+ std::string(ipfs_record_name_registry_state->data.begin(),
+ ipfs_record_name_registry_state->data.end())
+ .c_str());
+
+ GURL url(url_string);
+ if (!url.is_valid()) {
+ SetError(MakeInvalidParamsError());
+ return;
+ }
+
+ SetUrlResult(std::move(url));
}
void SnsResolverTask::RequestInternal(
diff --git a/components/brave_wallet/browser/sns_resolver_task.h b/components/brave_wallet/browser/sns_resolver_task.h
index f142fe1a046a..13bd7a5598db 100644
--- a/components/brave_wallet/browser/sns_resolver_task.h
+++ b/components/brave_wallet/browser/sns_resolver_task.h
@@ -58,6 +58,7 @@ struct SnsResolverTaskResult {
~SnsResolverTaskResult() = default;
SolanaAddress resolved_address;
+ GURL resolved_url;
};
struct SnsResolverTaskError {
@@ -91,7 +92,8 @@ class SnsResolverTask {
SnsResolverTask(DoneCallback done_callback,
APIRequestHelper* api_request_helper,
const std::string& domain,
- const GURL& network_url);
+ const GURL& network_url,
+ bool resolve_address);
SnsResolverTask(const SnsResolverTask&) = delete;
SnsResolverTask& operator=(const SnsResolverTask&) = delete;
~SnsResolverTask();
@@ -113,13 +115,22 @@ class SnsResolverTask {
void FetchSolRecordRegistryState();
void OnFetchSolRecordRegistryState(APIRequestResult api_request_result);
+ void FetchUrlRecordRegistryState();
+ void OnFetchUrlRecordRegistryState(APIRequestResult api_request_result);
+ void FetchIpfsRecordRegistryState();
+ void OnFetchIpfsRecordRegistryState(APIRequestResult api_request_result);
+
private:
template
friend class SnsResolverTaskContainer;
friend class ScopedWorkOnSnsTask;
void ScheduleWorkOnTask();
void WorkOnTask();
+ void WorkOnWalletAddressTask();
+ void WorkOnDomainResolveTask();
+ void SetAddressResult(SolanaAddress address);
+ void SetUrlResult(GURL url);
void SetError(SnsResolverTaskError error);
void NftOwnerDone(absl::optional nft_owner);
@@ -132,6 +143,7 @@ class SnsResolverTask {
raw_ptr api_request_helper_;
std::string domain_;
GURL network_url_;
+ bool resolve_address_ = false;
absl::optional domain_address_;
@@ -141,7 +153,8 @@ class SnsResolverTask {
absl::optional domain_name_registry_state_;
SolanaAddress sol_record_address_;
- absl::optional sol_record_name_registry_state_;
+
+ bool url_record_check_done_ = false;
absl::optional task_result_;
absl::optional task_error_;
diff --git a/components/decentralized_dns/content/BUILD.gn b/components/decentralized_dns/content/BUILD.gn
index 95295b533fd1..58b02d4677d9 100644
--- a/components/decentralized_dns/content/BUILD.gn
+++ b/components/decentralized_dns/content/BUILD.gn
@@ -19,6 +19,7 @@ static_library("content") {
deps = [
"//base",
+ "//brave/components/brave_wallet/common:common",
"//brave/components/decentralized_dns/core",
"//brave/components/l10n/common",
"//brave/components/resources:static_resources",
diff --git a/components/decentralized_dns/content/decentralized_dns_interstitial_controller_client.cc b/components/decentralized_dns/content/decentralized_dns_interstitial_controller_client.cc
index 183c011e4dfa..a309885ad60f 100644
--- a/components/decentralized_dns/content/decentralized_dns_interstitial_controller_client.cc
+++ b/components/decentralized_dns/content/decentralized_dns_interstitial_controller_client.cc
@@ -5,6 +5,7 @@
#include "brave/components/decentralized_dns/content/decentralized_dns_interstitial_controller_client.h"
+#include "base/notreached.h"
#include "brave/components/decentralized_dns/core/constants.h"
#include "brave/components/decentralized_dns/core/pref_names.h"
#include "brave/components/decentralized_dns/core/utils.h"
@@ -44,7 +45,7 @@ DecentralizedDnsInterstitialControllerClient::
local_state_(local_state) {}
void DecentralizedDnsInterstitialControllerClient::Proceed() {
- SetResolveMethodAndReload(ResolveMethodTypes::ETHEREUM);
+ SetResolveMethodAndReload(ResolveMethodTypes::ENABLED);
}
void DecentralizedDnsInterstitialControllerClient::DontProceed() {
@@ -54,9 +55,18 @@ void DecentralizedDnsInterstitialControllerClient::DontProceed() {
void DecentralizedDnsInterstitialControllerClient::SetResolveMethodAndReload(
ResolveMethodTypes type) {
DCHECK(local_state_);
- auto* pref_name = IsUnstoppableDomainsTLD(request_url_)
- ? kUnstoppableDomainsResolveMethod
- : kENSResolveMethod;
+ const char* pref_name = nullptr;
+ if (IsUnstoppableDomainsTLD(request_url_)) {
+ pref_name = kUnstoppableDomainsResolveMethod;
+ } else if (IsENSTLD(request_url_)) {
+ pref_name = kENSResolveMethod;
+ } else if (IsSnsTLD(request_url_)) {
+ pref_name = kSnsResolveMethod;
+ } else {
+ NOTREACHED();
+ return;
+ }
+
local_state_->SetInteger(pref_name, static_cast(type));
web_contents_->GetController().Reload(content::ReloadType::BYPASSING_CACHE,
true);
diff --git a/components/decentralized_dns/content/decentralized_dns_navigation_throttle.cc b/components/decentralized_dns/content/decentralized_dns_navigation_throttle.cc
index 4c97cb6c8b7e..148b37b2d4dc 100644
--- a/components/decentralized_dns/content/decentralized_dns_navigation_throttle.cc
+++ b/components/decentralized_dns/content/decentralized_dns_navigation_throttle.cc
@@ -8,7 +8,9 @@
#include
#include "base/bind.h"
+#include "base/features.h"
#include "base/threading/sequenced_task_runner_handle.h"
+#include "brave/components/brave_wallet/common/features.h"
#include "brave/components/decentralized_dns/content/decentralized_dns_interstitial_controller_client.h"
#include "brave/components/decentralized_dns/content/decentralized_dns_opt_in_page.h"
#include "brave/components/decentralized_dns/content/ens_offchain_lookup_interstitial_controller_client.h"
@@ -59,7 +61,10 @@ DecentralizedDnsNavigationThrottle::WillStartRequest() {
GURL url = navigation_handle()->GetURL();
if ((IsUnstoppableDomainsTLD(url) &&
IsUnstoppableDomainsResolveMethodAsk(local_state_)) ||
- (IsENSTLD(url) && IsENSResolveMethodAsk(local_state_))) {
+ (IsENSTLD(url) && IsENSResolveMethodAsk(local_state_)) ||
+ (base::FeatureList::IsEnabled(
+ brave_wallet::features::kBraveWalletSnsFeature) &&
+ IsSnsTLD(url) && IsSnsResolveMethodAsk(local_state_))) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&DecentralizedDnsNavigationThrottle::ShowInterstitial,
diff --git a/components/decentralized_dns/content/decentralized_dns_opt_in_page.cc b/components/decentralized_dns/content/decentralized_dns_opt_in_page.cc
index 1b90b23fe65d..32fe35cf5be6 100644
--- a/components/decentralized_dns/content/decentralized_dns_opt_in_page.cc
+++ b/components/decentralized_dns/content/decentralized_dns_opt_in_page.cc
@@ -67,18 +67,13 @@ void DecentralizedDnsOptInPage::CommandReceived(const std::string& command) {
void DecentralizedDnsOptInPage::PopulateInterstitialStrings(
base::Value::Dict& load_time_data) {
- const std::vector message_params = {
- u"",
+ const std::vector infura_links = {
+ u"https://consensys.net/terms-of-use/",
+ u"https://consensys.net/privacy-policy/"};
- u"",
-
- u"",
-
- u"",
- };
+ const std::vector coinbase_links = {
+ u"https://www.coinbase.com/legal/cloud/terms-of-service/",
+ u"https://www.coinbase.com/legal/privacy/"};
if (IsUnstoppableDomainsTLD(request_url_)) {
load_time_data.Set("tabTitle", brave_l10n::GetLocalizedResourceUTF16String(
@@ -91,8 +86,8 @@ void DecentralizedDnsOptInPage::PopulateInterstitialStrings(
base::ReplaceStringPlaceholders(
brave_l10n::GetLocalizedResourceUTF16String(
IDS_UNSTOPPABLE_DOMAINS_OPT_IN_PRIMARY_PARAGRAPH),
- message_params, nullptr));
- } else {
+ infura_links, nullptr));
+ } else if (IsENSTLD(request_url_)) {
load_time_data.Set("tabTitle", brave_l10n::GetLocalizedResourceUTF16String(
IDS_ENS_OPT_IN_TITLE));
load_time_data.Set("heading", brave_l10n::GetLocalizedResourceUTF16String(
@@ -101,12 +96,31 @@ void DecentralizedDnsOptInPage::PopulateInterstitialStrings(
base::ReplaceStringPlaceholders(
brave_l10n::GetLocalizedResourceUTF16String(
IDS_ENS_OPT_IN_PRIMARY_PARAGRAPH),
- message_params, nullptr));
+ infura_links, nullptr));
+ } else if (IsSnsTLD(request_url_)) {
+ load_time_data.Set("tabTitle", brave_l10n::GetLocalizedResourceUTF16String(
+ IDS_SNS_OPT_IN_TITLE));
+ load_time_data.Set("heading", brave_l10n::GetLocalizedResourceUTF16String(
+ IDS_SNS_OPT_IN_HEADING));
+ load_time_data.Set("primaryParagraph",
+ base::ReplaceStringPlaceholders(
+ brave_l10n::GetLocalizedResourceUTF16String(
+ IDS_SNS_OPT_IN_PRIMARY_PARAGRAPH),
+ coinbase_links, nullptr));
+ } else {
+ NOTREACHED();
}
- load_time_data.Set("primaryButtonText",
- brave_l10n::GetLocalizedResourceUTF16String(
- IDS_DECENTRALIZED_DNS_OPT_IN_PRIMARY_BUTTON));
+ if (IsSnsTLD(request_url_)) {
+ load_time_data.Set(
+ "primaryButtonText",
+ brave_l10n::GetLocalizedResourceUTF16String(
+ IDS_DECENTRALIZED_DNS_OPT_IN_PRIMARY_COINBASE_BUTTON));
+ } else {
+ load_time_data.Set("primaryButtonText",
+ brave_l10n::GetLocalizedResourceUTF16String(
+ IDS_DECENTRALIZED_DNS_OPT_IN_PRIMARY_INFURA_BUTTON));
+ }
load_time_data.Set("dontProceedButtonText",
brave_l10n::GetLocalizedResourceUTF16String(
IDS_DECENTRALIZED_DNS_OPT_IN_DONT_PROCEED_BUTTON));
diff --git a/components/decentralized_dns/content/ens_offchain_lookup_opt_in_page.cc b/components/decentralized_dns/content/ens_offchain_lookup_opt_in_page.cc
index c409f5122713..528ffbda887b 100644
--- a/components/decentralized_dns/content/ens_offchain_lookup_opt_in_page.cc
+++ b/components/decentralized_dns/content/ens_offchain_lookup_opt_in_page.cc
@@ -67,13 +67,8 @@ void EnsOffchainLookupOptInPage::CommandReceived(const std::string& command) {
void EnsOffchainLookupOptInPage::PopulateInterstitialStrings(
base::Value::Dict& load_time_data) {
- const std::vector message_params = {
- u"",
-
- u"",
- };
+ std::u16string learn_more_link =
+ u"https://github.com/brave/brave-browser/wiki/ENS-offchain-lookup";
load_time_data.Set("tabTitle", brave_l10n::GetLocalizedResourceUTF16String(
IDS_ENS_OFFCHAIN_LOOKUP_OPT_IN_TITLE));
@@ -83,7 +78,7 @@ void EnsOffchainLookupOptInPage::PopulateInterstitialStrings(
base::ReplaceStringPlaceholders(
brave_l10n::GetLocalizedResourceUTF16String(
IDS_ENS_OFFCHAIN_LOOKUP_OPT_IN_PRIMARY_PARAGRAPH),
- message_params, nullptr));
+ {learn_more_link}, nullptr));
load_time_data.Set("primaryButtonText",
brave_l10n::GetLocalizedResourceUTF16String(
diff --git a/components/decentralized_dns/core/constants.h b/components/decentralized_dns/core/constants.h
index 569fe5280aad..fa594106306d 100644
--- a/components/decentralized_dns/core/constants.h
+++ b/components/decentralized_dns/core/constants.h
@@ -13,8 +13,8 @@ enum class ResolveMethodTypes {
ASK,
DISABLED,
DEPRECATED_DNS_OVER_HTTPS,
- ETHEREUM,
- MAX_VALUE = ETHEREUM,
+ ENABLED,
+ MAX_VALUE = ENABLED,
};
enum class EnsOffchainResolveMethod { kAsk, kDisabled, kEnabled };
diff --git a/components/decentralized_dns/core/pref_names.h b/components/decentralized_dns/core/pref_names.h
index 3aa712672245..7a91429db1c7 100644
--- a/components/decentralized_dns/core/pref_names.h
+++ b/components/decentralized_dns/core/pref_names.h
@@ -12,7 +12,7 @@ namespace decentralized_dns {
// domains, between:
// Disabled: Disable all unstoppable domains resolution.
// Ask: Ask users if they want to enable support of unstoppable domains.
-// Ethereum: Resolve domain name using Ethereum JSON-RPC server.
+// Enabled: Resolve domain name using Ethereum JSON-RPC server.
constexpr char kUnstoppableDomainsResolveMethod[] =
"brave.unstoppable_domains.resolve_method";
@@ -20,12 +20,19 @@ constexpr char kUnstoppableDomainsResolveMethod[] =
// between:
// Disabled: Disable all ENS domains resolution.
// Ask: Ask users if they want to enable support of ENS.
-// Ethereum: Resolve domain name using Ethereum JSON-RPC server.
+// Enabled: Resolve domain name using Ethereum JSON-RPC server.
constexpr char kENSResolveMethod[] = "brave.ens.resolve_method";
constexpr char kEnsOffchainResolveMethod[] =
"brave.ens.offchain_resolve_method";
+// Used to determine which method should be used to resolve SNS domains,
+// between:
+// Disabled: Disable all SNS domains resolution.
+// Ask: Ask users if they want to enable support of SNS.
+// Enabled: Resolve domain name using Solana JSON-RPC server.
+constexpr char kSnsResolveMethod[] = "brave.sns.resolve_method";
+
} // namespace decentralized_dns
#endif // BRAVE_COMPONENTS_DECENTRALIZED_DNS_CORE_PREF_NAMES_H_
diff --git a/components/decentralized_dns/core/utils.cc b/components/decentralized_dns/core/utils.cc
index 7eb0c3466fa1..b41d180230eb 100644
--- a/components/decentralized_dns/core/utils.cc
+++ b/components/decentralized_dns/core/utils.cc
@@ -23,6 +23,8 @@ void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(
kEnsOffchainResolveMethod,
static_cast(EnsOffchainResolveMethod::kAsk));
+ registry->RegisterIntegerPref(kSnsResolveMethod,
+ static_cast(ResolveMethodTypes::ASK));
}
void MigrateObsoleteLocalStatePrefs(PrefService* local_state) {
@@ -61,7 +63,7 @@ bool IsUnstoppableDomainsResolveMethodEthereum(PrefService* local_state) {
}
return local_state->GetInteger(kUnstoppableDomainsResolveMethod) ==
- static_cast(ResolveMethodTypes::ETHEREUM);
+ static_cast(ResolveMethodTypes::ENABLED);
}
bool IsENSTLD(const GURL& url) {
@@ -83,7 +85,7 @@ bool IsENSResolveMethodEthereum(PrefService* local_state) {
}
return local_state->GetInteger(kENSResolveMethod) ==
- static_cast(ResolveMethodTypes::ETHEREUM);
+ static_cast(ResolveMethodTypes::ENABLED);
}
void SetEnsOffchainResolveMethod(PrefService* local_state,
@@ -96,4 +98,26 @@ EnsOffchainResolveMethod GetEnsOffchainResolveMethod(PrefService* local_state) {
local_state->GetInteger(kEnsOffchainResolveMethod));
}
+bool IsSnsTLD(const GURL& url) {
+ return base::EndsWith(url.host_piece(), kSolDomain);
+}
+
+bool IsSnsResolveMethodAsk(PrefService* local_state) {
+ if (!local_state) {
+ return false; // Treat it as disabled.
+ }
+
+ return local_state->GetInteger(kSnsResolveMethod) ==
+ static_cast(ResolveMethodTypes::ASK);
+}
+
+bool IsSnsResolveMethodEnabled(PrefService* local_state) {
+ if (!local_state) {
+ return false; // Treat it as disabled.
+ }
+
+ return local_state->GetInteger(kSnsResolveMethod) ==
+ static_cast(ResolveMethodTypes::ENABLED);
+}
+
} // namespace decentralized_dns
diff --git a/components/decentralized_dns/core/utils.h b/components/decentralized_dns/core/utils.h
index 758f257ae6bb..a0deaa9b77a6 100644
--- a/components/decentralized_dns/core/utils.h
+++ b/components/decentralized_dns/core/utils.h
@@ -29,6 +29,10 @@ void SetEnsOffchainResolveMethod(PrefService* local_state,
EnsOffchainResolveMethod method);
EnsOffchainResolveMethod GetEnsOffchainResolveMethod(PrefService* local_state);
+bool IsSnsTLD(const GURL& url);
+bool IsSnsResolveMethodAsk(PrefService* local_state);
+bool IsSnsResolveMethodEnabled(PrefService* local_state);
+
} // namespace decentralized_dns
#endif // BRAVE_COMPONENTS_DECENTRALIZED_DNS_CORE_UTILS_H_
diff --git a/components/resources/decentralized_dns_strings.grdp b/components/resources/decentralized_dns_strings.grdp
index 4a1ae9e4427f..241d36a548b9 100644
--- a/components/resources/decentralized_dns_strings.grdp
+++ b/components/resources/decentralized_dns_strings.grdp
@@ -7,7 +7,7 @@
Enable support of Unstoppable Domains in Brave?
- Brave will be using Infura to issue Ethereum JSON-RPC calls to the smart contract from Unstoppable Domains to resolve .crypto (and also .x, .coin, .nft, .dao, .wallet, .blockchain, .bitcoin, .zil) domain name lookup requests. If you enable this, Infura will see those specific domains that you're trying to visit but they will not be able to see other domains. See Infura's $1terms of use$2 and $3privacy policy$4.
+ Brave will be using Infura to issue Ethereum JSON-RPC calls to the smart contract from Unstoppable Domains to resolve .crypto (and also .x, .coin, .nft, .dao, .wallet, .blockchain, .bitcoin, .zil) domain name lookup requests. If you enable this, Infura will see those specific domains that you're trying to visit but they will not be able to see other domains. See Infura's <a target="_blank" href="$1">terms of use</a> and <a target="_blank" href="$2">privacy policy</a>.
Ethereum Name Service
@@ -16,7 +16,7 @@
Enable support of Ethereum Name Service (ENS) in Brave?
- Brave will be using Infura to issue Ethereum JSON-RPC calls to the smart contract from Ethereum Name Service to resolve .eth domain name lookup requests. If you enable this, Infura will see the .eth domain that you're trying to visit but they will not be able to see other domains. See Infura's $1terms of use$2 and $3privacy policy$4.
+ Brave will be using Infura to issue Ethereum JSON-RPC calls to the smart contract from Ethereum Name Service to resolve .eth domain name lookup requests. If you enable this, Infura will see the .eth domain that you're trying to visit but they will not be able to see other domains. See Infura's <a target="_blank" href="$1">terms of use</a> and <a target="_blank" href="$2">privacy policy</a>.
Ethereum Name Service Offchain Lookup
@@ -25,7 +25,7 @@
Enable support of Ethereum Name Service (ENS) offchain lookup in Brave?
- Brave will be using offchain gateways to resolve .eth domain name lookup requests. If you enable this, third party gateways will see the .eth domain that you're trying to visit but they will not be able to see other domains. $1Learn more$2.
+ Brave will be using offchain gateways to resolve .eth domain name lookup requests. If you enable this, third party gateways will see the .eth domain that you're trying to visit but they will not be able to see other domains. <a target="_blank" href="$1">Learn more</a>.
Proceed with offchain lookup
@@ -33,9 +33,24 @@
Disable
+
+ Solana Name Service
+
+
+ Enable support of Solana Name Service (SNS) in Brave?
+
+
+ Brave will be using Coinbase Cloud to issue Solana JSON-RPC calls to the program from Solana Name Service to resolve .sol domain name lookup requests. If you enable this, Coinbase Cloud will see the .sol domain that you're trying to visit but they will not be able to see other domains. See Coinbase Cloud's <a target="_blank" href="$1">terms of use</a> and <a target="_blank" href="$2">privacy policy</a>.
+
Proceed using Infura server
+
+ Proceed using Infura server
+
+
+ Proceed using Coinbase Cloud server
+
Disable
@@ -45,8 +60,8 @@
Disabled
-
- Ethereum
+
+ Enabled
Ask
diff --git a/net/decentralized_dns/constants.h b/net/decentralized_dns/constants.h
index aa18fa5fb31d..41a7eb1a57e5 100644
--- a/net/decentralized_dns/constants.h
+++ b/net/decentralized_dns/constants.h
@@ -15,6 +15,8 @@ constexpr const char* kUnstoppableDomains[] = {
constexpr char kEthDomain[] = ".eth";
constexpr char kDNSForEthDomain[] = ".eth.link";
+constexpr char kSolDomain[] = ".sol";
+
} // namespace decentralized_dns
#endif // BRAVE_NET_DECENTRALIZED_DNS_CONSTANTS_H_