From e33694bb5d20913722293ca06f04e6e83d2d8504 Mon Sep 17 00:00:00 2001 From: brave-builds <45370463+brave-builds@users.noreply.github.com> Date: Mon, 20 Mar 2023 19:20:32 +0100 Subject: [PATCH] Fix connecting Brave Wallet to snapshot.org (uplift to 1.49.x) (#17665) Uplift of #17658 (squashed) to release --- .../send_or_sign_transaction_browsertest.cc | 14 +++++ .../renderer/js_ethereum_provider.cc | 51 ++++++++++++++++--- .../send_or_sign_transaction.html | 8 +++ 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/browser/brave_wallet/send_or_sign_transaction_browsertest.cc b/browser/brave_wallet/send_or_sign_transaction_browsertest.cc index c6813a277636..3cd0f98220af 100644 --- a/browser/brave_wallet/send_or_sign_transaction_browsertest.cc +++ b/browser/brave_wallet/send_or_sign_transaction_browsertest.cc @@ -892,6 +892,20 @@ IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, IsConnected) { .ExtractBool()); } +IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, CallViaProxy) { + RestoreWallet(); + 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(), "getIsConnectedViaProxy()", + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractBool()); + EXPECT_TRUE(EvalJs(web_contents(), "getIsBraveWalletViaProxy()", + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractBool()); +} + IN_PROC_BROWSER_TEST_F(SendOrSignTransactionBrowserTest, EthSendTransactionEIP1559Tx) { SetNetworkForTesting("0x1"); // mainnet diff --git a/components/brave_wallet/renderer/js_ethereum_provider.cc b/components/brave_wallet/renderer/js_ethereum_provider.cc index b257aeff3de4..43075bbb704d 100644 --- a/components/brave_wallet/renderer/js_ethereum_provider.cc +++ b/components/brave_wallet/renderer/js_ethereum_provider.cc @@ -39,6 +39,25 @@ constexpr char kEthereum[] = "ethereum"; constexpr char kEmit[] = "emit"; constexpr char kIsBraveWallet[] = "isBraveWallet"; constexpr char kEthereumProviderScript[] = "ethereum_provider.js"; +constexpr char kEthereumProxyHandlerScript[] = R"((function() { + const handler = { + get: (target, property, receiver) => { + if (typeof target[property] === 'function' && + (property === 'request' || property === 'isConnected' || + property === 'enable' || property === 'sendAsync' || + property === 'send')) { + return new Proxy(target[property], { + apply: (targetFunc, thisArg, args) => { + return targetFunc.call(target, ...args); + } + }); + } + return target[property]; + } + }; + return handler; +})())"; +constexpr char kEthereumProxyScript[] = "ethereum_proxy.js"; constexpr char kIsMetaMask[] = "isMetaMask"; constexpr char kMetaMask[] = "_metamask"; constexpr char kIsUnlocked[] = "isUnlocked"; @@ -178,19 +197,38 @@ void JSEthereumProvider::Install(bool allow_overwrite_window_ethereum_provider, return; } v8::Local provider_value = provider.ToV8(); + v8::Local provider_object = + provider_value->ToObject(context).ToLocalChecked(); + + // Create a proxy to the actual JSEthereumProvider object which will be + // exposed via window.ethereum. + // This proxy uses a handler which would call things directly on the actual + // JSEthereumProvider object so dApps which creates and uses their own proxy + // of window.ethereum to access our provider won't throw a "Illegal + // invocation: Function must be called on an object of type + // JSEthereumProvider" error. + blink::WebLocalFrame* web_frame = render_frame->GetWebFrame(); + v8::Local ethereum_proxy; + auto ethereum_proxy_handler_val = ExecuteScript( + web_frame, kEthereumProxyHandlerScript, kEthereumProxyScript); + v8::Local ethereum_proxy_handler_obj = + ethereum_proxy_handler_val.ToLocalChecked() + ->ToObject(context) + .ToLocalChecked(); + if (!v8::Proxy::New(context, provider_object, ethereum_proxy_handler_obj) + .ToLocal(ðereum_proxy)) { + return; + } if (!allow_overwrite_window_ethereum_provider) { - SetProviderNonWritable(context, global, provider_value, + SetProviderNonWritable(context, global, ethereum_proxy, gin::StringToV8(isolate, kEthereum), true); } else { global - ->Set(context, gin::StringToSymbol(isolate, kEthereum), provider_value) + ->Set(context, gin::StringToSymbol(isolate, kEthereum), ethereum_proxy) .Check(); } - v8::Local provider_object = - provider_value->ToObject(context).ToLocalChecked(); - // Non-function properties are readonly guaranteed by gin::Wrappable // send should be writable because of // https://github.com/brave/brave-browser/issues/25078 @@ -226,7 +264,6 @@ void JSEthereumProvider::Install(bool allow_overwrite_window_ethereum_provider, SetOwnPropertyWritable(context, metamask_obj, gin::StringToV8(isolate, kIsUnlocked), false); - blink::WebLocalFrame* web_frame = render_frame->GetWebFrame(); ExecuteScript(web_frame, LoadDataResource( IDR_BRAVE_WALLET_SCRIPT_ETHEREUM_PROVIDER_SCRIPT_BUNDLE_JS), @@ -272,6 +309,8 @@ v8::Local JSEthereumProvider::GetSelectedAddress( gin::ObjectTemplateBuilder JSEthereumProvider::GetObjectTemplateBuilder( v8::Isolate* isolate) { + // Note: When adding a new method, you would need to update the list in + // kEthereumProxyHandlerScript too otherwise the function call would fail. return gin::Wrappable::GetObjectTemplateBuilder(isolate) .SetProperty(kIsBraveWallet, &JSEthereumProvider::GetIsBraveWallet) .SetProperty(kIsMetaMask, &JSEthereumProvider::GetIsMetaMask) diff --git a/test/data/brave-wallet/send_or_sign_transaction.html b/test/data/brave-wallet/send_or_sign_transaction.html index 2d9a17237568..4d8635763b51 100644 --- a/test/data/brave-wallet/send_or_sign_transaction.html +++ b/test/data/brave-wallet/send_or_sign_transaction.html @@ -102,6 +102,14 @@ function getIsConnected() { window.domAutomationController.send(window.ethereum.isConnected()) } + function getIsConnectedViaProxy() { + proxy = new Proxy(window.ethereum, {}) + window.domAutomationController.send(proxy.isConnected()) + } + function getIsBraveWalletViaProxy() { + proxy = new Proxy(window.ethereum, {}) + window.domAutomationController.send(proxy.isBraveWallet) + }