Allow dApps to suggest change of RPC (EIP-3085 EIP-3326) (#13716)

This commit is contained in:
frank 2022-10-14 16:32:55 +08:00 committed by GitHub
parent af3ba74e30
commit a74a44e492
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 278 additions and 17 deletions

View File

@ -22,7 +22,9 @@
[status-im.bottom-sheet.core :as bottom-sheet] [status-im.bottom-sheet.core :as bottom-sheet]
[status-im.browser.webview-ref :as webview-ref] [status-im.browser.webview-ref :as webview-ref]
["eth-phishing-detect" :as eth-phishing-detect] ["eth-phishing-detect" :as eth-phishing-detect]
[status-im.utils.debounce :as debounce])) [status-im.utils.debounce :as debounce]
[status-im.browser.eip3085 :as eip3085]
[status-im.browser.eip3326 :as eip3326]))
(fx/defn update-browser-option (fx/defn update-browser-option
[{:keys [db]} option-key option-value] [{:keys [db]} option-key option-value]
@ -345,7 +347,7 @@
constants/web3-eth-sign constants/web3-keycard-sign-typed-data} method)) constants/web3-eth-sign constants/web3-keycard-sign-typed-data} method))
(fx/defn web3-send-async (fx/defn web3-send-async
[cofx {:keys [method params id] :as payload} message-id] [cofx dapp-name {:keys [method params id] :as payload} message-id]
(let [message? (web3-sign-message? method) (let [message? (web3-sign-message? method)
dapps-address (get-in cofx [:db :multiaccount :dapps-address]) dapps-address (get-in cofx [:db :multiaccount :dapps-address])
typed? (and (not= constants/web3-personal-sign method) (not= constants/web3-eth-sign method))] typed? (and (not= constants/web3-personal-sign method) (not= constants/web3-eth-sign method))]
@ -371,25 +373,33 @@
(dissoc :gasPrice))}) (dissoc :gasPrice))})
{:on-result [:browser.dapp/transaction-on-result message-id id] {:on-result [:browser.dapp/transaction-on-result message-id id]
:on-error [:browser.dapp/transaction-on-error message-id]})))) :on-error [:browser.dapp/transaction-on-error message-id]}))))
(if (#{"eth_accounts" "eth_coinbase"} method) (cond
(#{"eth_accounts" "eth_coinbase"} method)
(send-to-bridge cofx {:type constants/web3-send-async-callback (send-to-bridge cofx {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:result {:jsonrpc "2.0" :result {:jsonrpc "2.0"
:id (int id) :id (int id)
:result (if (= method "eth_coinbase") dapps-address [dapps-address])}}) :result (if (= method "eth_coinbase") dapps-address [dapps-address])}})
(if (= method "personal_ecRecover") (= method "personal_ecRecover")
{:signing.fx/recover-message {:params {:message (first params) {:signing.fx/recover-message {:params {:message (first params)
:signature (second params)} :signature (second params)}
:on-completed #(re-frame/dispatch [:browser.callback/call-rpc :on-completed #(re-frame/dispatch [:browser.callback/call-rpc
{:type constants/web3-send-async-callback {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:result (types/json->clj %)}])}} :result (types/json->clj %)}])}}
(= method "wallet_switchEthereumChain")
(eip3326/handle-switch-ethereum-chain cofx dapp-name id message-id (first params))
(= method "wallet_addEthereumChain")
(eip3085/handle-add-ethereum-chain cofx dapp-name id message-id (first params))
:else
{:browser/call-rpc [payload {:browser/call-rpc [payload
#(re-frame/dispatch [:browser.callback/call-rpc #(re-frame/dispatch [:browser.callback/call-rpc
{:type constants/web3-send-async-callback {:type constants/web3-send-async-callback
:messageId message-id :messageId message-id
:error %1 :error %1
:result %2}])]}))))) :result %2}])]}))))
(fx/defn handle-no-permissions [cofx {:keys [method id]} message-id] (fx/defn handle-no-permissions [cofx {:keys [method id]} message-id]
(if (= method "eth_accounts") (if (= method "eth_accounts")
@ -419,7 +429,7 @@
[{:keys [db] :as cofx} dapp-name {:keys [method] :as payload} message-id] [{:keys [db] :as cofx} dapp-name {:keys [method] :as payload} message-id]
(if (has-permissions? db dapp-name method) (if (has-permissions? db dapp-name method)
(handle-no-permissions cofx payload message-id) (handle-no-permissions cofx payload message-id)
(web3-send-async cofx payload message-id))) (web3-send-async cofx dapp-name payload message-id)))
(fx/defn handle-scanned-qr-code (fx/defn handle-scanned-qr-code
{:events [:browser.bridge.callback/qr-code-scanned]} {:events [:browser.bridge.callback/qr-code-scanned]}

View File

@ -0,0 +1,69 @@
;reference https://eips.ethereum.org/EIPS/eip-3085 EIP-3085: Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`)
(ns status-im.browser.eip3085
(:require [status-im.constants :as constants]
[clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.utils.fx :as fx]
[status-im.network.core :as network]
[taoensso.timbre :as log]
[status-im.utils.random :as random]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.ui.screens.browser.eip3085.sheet :as sheet]))
(fx/defn send-success-call-to-bridge
{:events [:eip3085/send-success-call-to-bridge]}
[_ id messageId]
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId messageId
:result {:jsonrpc "2.0"
:id (int id)
:result nil}}})
(fx/defn allow-permission
{:events [:eip3085.ui/dapp-permission-allowed]}
[{:keys [db] :as cofx} message-id {:keys [new-networks id]}]
{:db (assoc db :networks/networks new-networks)
::json-rpc/call [{:method "settings_saveSetting"
:params [:networks/networks (vals new-networks)]
:on-success #(re-frame/dispatch [:eip3085/send-success-call-to-bridge cofx id message-id])
:on-error #(log/error "failed to perform settings_saveSetting" %)}]
:dispatch [:bottom-sheet/hide]})
(fx/defn deny-permission
{:events [:eip3085.ui/dapp-permission-denied]}
[_ message-id _]
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code 4001
:message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]})
(fx/defn handle-add-ethereum-chain
{:events [:eip3085/handle-add-ethereum-chain]}
[{{:networks/keys [networks] :as db} :db :as cofx}
dapp-name id message-id {:keys [chainId blockExplorerUrls chainName iconUrls nativeCurrency rpcUrls] :as params}]
(let [manage {:name {:value chainName}
:symbol {:value (:symbol nativeCurrency)}
:url {:value (first rpcUrls)}
:network-id {:value chainId}
:chain {:value :custom}}]
(if (network/valid-manage? manage)
(let [{:keys [name url chain network-id symbol]} manage
random-id (string/replace (random/id) "-" "")
network (network/new-network random-id
(:value name)
(:value symbol)
(:value url)
(:value chain)
(:value network-id))
new-networks (assoc networks random-id network)
params (assoc params :new-networks new-networks :id id :new-network network)]
(if (network/chain-id-available? networks network)
{:dispatch [:bottom-sheet/show-sheet {:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}
(send-success-call-to-bridge cofx id message-id)))
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code -32602
:message "invalid network parameters"}}})))

View File

@ -0,0 +1,43 @@
;reference https://eips.ethereum.org/EIPS/eip-3326 EIP-3326: Wallet Switch Ethereum Chain RPC Method (`wallet_switchEthereumChain`)
(ns status-im.browser.eip3326
(:require [status-im.constants :as constants]
[status-im.utils.fx :as fx]
[status-im.ethereum.core :as ethereum]
[status-im.ui.screens.browser.eip3326.sheet :as sheet]))
(fx/defn deny-permission
{:events [:eip3326.ui/dapp-permission-denied]}
[{:keys [db] :as cofx} message-id _]
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code 4001
:message "User rejected the request."}}
:dispatch [:bottom-sheet/hide]})
(fx/defn handle-switch-ethereum-chain
{:events [:eip3326/handle-switch-ethereum-chain]}
[{:keys [db] :as cofx} dapp-name id message-id {:keys [chainId] :as params}]
(let [target-chain-id (js/parseInt chainId 16)
networks (vals (get-in db [:networks/networks]))
exist-chain-ids (set (map ethereum/network->chain-id networks))
current-chain-id (ethereum/chain-id db)]
(if (exist-chain-ids target-chain-id)
(if (= current-chain-id target-chain-id)
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result nil}}}
(let [target-network (first (filter #(= (ethereum/network->chain-id %1) target-chain-id) networks))
target-network-id (:id target-network)
current-network (ethereum/current-network db)
network-from (:name current-network)
network-to (:name target-network)
params (assoc params :target-network-id target-network-id :network-from network-from :network-to network-to)]
{:dispatch [:bottom-sheet/show-sheet {:content (fn []
[sheet/permissions-panel dapp-name message-id params])}]}))
{:browser/send-to-bridge {:type constants/web3-send-async-callback
:messageId message-id
:error {:code 4902
:message (str "Unrecognized chain ID: " target-chain-id ". Try adding the chain using wallet_addEthereumChain first.")}}})))

View File

@ -0,0 +1,81 @@
(ns status-im.ui.screens.browser.eip3085.sheet
(:require [re-frame.core :as re-frame]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.styles :as styles]
[quo.design-system.colors :as colors]
[quo.core :as quo]
[status-im.ui.components.copyable-text :as copyable-text])
(:require-macros [status-im.utils.views :as views]))
(views/defview permissions-panel [dapp-name message-id params]
(views/letsubs [{:keys [dapp? dapp]} [:get-current-browser]]
[react/view {}
[react/view styles/permissions-panel-icons-container
(if dapp?
[chat-icon.screen/dapp-icon-permission dapp 40]
[react/view styles/permissions-panel-dapp-icon-container
[icons/icon :main-icons/dapp {:color colors/gray}]])
[react/view {:margin-left 8 :margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 8}
[react/view styles/dot]]
[react/view styles/permissions-panel-ok-icon-container
[icons/icon :tiny-icons/tiny-check styles/permissions-panel-ok-ico]]
[react/view {:margin-left 8 :margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 8}
[react/view styles/dot]]
[react/view styles/permissions-panel-wallet-icon-container
[icons/icon :main-icons/wallet {:color colors/white}]]]
[react/text {:style styles/permissions-panel-title-label :number-of-lines 2}
(str "\"" dapp-name "\" Allow this site to add a network?")]
[react/text {:style styles/permissions-panel-description-label :number-of-lines 4} "This will allow this network to be used within Status.Status does not verify custom networks.Learn about scams and network security risks."]
[react/scroll-view
[copyable-text/copyable-text-view
{:copied-text (:name (:new-network params))}
[quo/list-item
{:size :small
:accessibility-label :network-name
:title "Network Name"
:accessory :text
:accessory-text (:name (:new-network params))}]]
[copyable-text/copyable-text-view
{:copied-text (get-in params [:new-network :config :UpstreamConfig :URL])}
[quo/list-item
{:size :small
:accessibility-label :network-url
:title "Network URL"
:accessory :text
:accessory-text (get-in params [:new-network :config :UpstreamConfig :URL])}]]
[copyable-text/copyable-text-view
{:copied-text (str (get-in params [:new-network :config :NetworkId]))}
[quo/list-item
{:size :small
:accessibility-label :network-id
:title "Chain ID"
:accessory :text
:accessory-text (str (get-in params [:new-network :config :NetworkId]))}]]]
[react/view {:style {:flex-direction :row
:justify-content :center
:margin-horizontal 8
:margin-top 24}}
[react/view {:flex 1
:margin-horizontal 8}
[quo/button
{:theme :negative
:on-press #(re-frame/dispatch [:eip3085.ui/dapp-permission-denied message-id params])}
(i18n/label :t/deny)]]
[react/view {:flex 1
:margin-horizontal 8}
[quo/button
{:theme :positive
:style {:margin-horizontal 8}
:on-press #(re-frame/dispatch [:eip3085.ui/dapp-permission-allowed message-id params])}
(i18n/label :t/allow)]]]]))

View File

@ -0,0 +1,58 @@
(ns status-im.ui.screens.browser.eip3326.sheet
(:require [re-frame.core :as re-frame]
[status-im.i18n.i18n :as i18n]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen]
[status-im.ui.components.icons.icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.browser.styles :as styles]
[status-im.utils.debounce :as debounce]
[status-im.network.core :as network]
[quo.design-system.colors :as colors]
[quo.core :as quo])
(:require-macros [status-im.utils.views :as views]))
(views/defview permissions-panel [dapp-name message-id {:keys [network-from network-to target-network-id] :as params}]
(views/letsubs [{:keys [dapp? dapp]} [:get-current-browser]]
[react/view {}
[react/view styles/permissions-panel-icons-container
(if dapp?
[chat-icon.screen/dapp-icon-permission dapp 40]
[react/view styles/permissions-panel-dapp-icon-container
[icons/icon :main-icons/dapp {:color colors/gray}]])
[react/view {:margin-left 8 :margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 8}
[react/view styles/dot]]
[react/view styles/permissions-panel-ok-icon-container
[icons/icon :tiny-icons/tiny-check styles/permissions-panel-ok-ico]]
[react/view {:margin-left 8 :margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 4}
[react/view styles/dot]]
[react/view {:margin-right 8}
[react/view styles/dot]]
[react/view styles/permissions-panel-wallet-icon-container
[icons/icon :main-icons/wallet {:color colors/white}]]]
[react/text {:style styles/permissions-panel-title-label :number-of-lines 2}
(str "\"" dapp-name "\" Allow this site to switch the network?")]
[react/text {:style styles/permissions-panel-description-label :number-of-lines 5}
(str "This will switch the selected network within Status to a previously added network:\n" network-from " -> " network-to "\nit will require login/logout")]
[react/view {:style {:flex-direction :row
:justify-content :center
:margin-horizontal 8
:margin-top 24}}
[react/view {:flex 1
:margin-horizontal 8}
[quo/button
{:theme :negative
:on-press #(re-frame/dispatch [:eip3326.ui/dapp-permission-denied message-id params])}
(i18n/label :t/deny)]]
[react/view {:flex 1
:margin-horizontal 8}
[quo/button
{:theme :positive
:style {:margin-horizontal 8}
:on-press #(debounce/dispatch-and-chill [::network/connect-network-pressed target-network-id] 1000)}
(i18n/label :t/allow)]]]]))