From ac4ffd83db223223f046e28f4382967e9a3cfcd5 Mon Sep 17 00:00:00 2001 From: Lungu Cristian Date: Tue, 2 Jul 2024 16:39:33 +0200 Subject: [PATCH] Wallet Connect session establish networks (#20576) * feat: showing supported networks in wc connection * feat: added :large size for quo/data-item * feat: added required networks check --- .../components/settings/data_item/style.cljs | 30 +++++-- .../preview/quo/settings/data_item.cljs | 3 +- .../contexts/wallet/wallet_connect/core.cljs | 33 +++++++ .../wallet/wallet_connect/events.cljs | 85 +++++++++++-------- .../wallet_connect/session_proposal/view.cljs | 76 +++++++++-------- src/status_im/subs/wallet/wallet_connect.cljs | 26 +++++- .../subs/wallet/wallet_connect_test.cljs | 66 +++++++------- translations/en.json | 2 + 8 files changed, 207 insertions(+), 114 deletions(-) diff --git a/src/quo/components/settings/data_item/style.cljs b/src/quo/components/settings/data_item/style.cljs index 71e049c3bb..dd82e6a0ff 100644 --- a/src/quo/components/settings/data_item/style.cljs +++ b/src/quo/components/settings/data_item/style.cljs @@ -6,8 +6,14 @@ [{:keys [size card? blur? actionable? theme]}] {:flex-direction :row :justify-content :space-between - :padding-vertical (when (= size :default) 8) - :padding-horizontal (when (= size :default) 12) + :padding-vertical (case size + :default 8 + :large 12 + nil) + :padding-horizontal (case size + :default 12 + :large 16 + nil) :border-radius 16 :border-width (when (and card? (not= size :small)) 1) :background-color (if blur? @@ -24,15 +30,27 @@ (defn loading-container [size blur? theme] - {:width (if (= size :default) 132 72) - :height (if (= size :default) 16 10) + {:width (case size + :large 132 + :small 72 + 132) + :height (case size + :large 16 + :small 10 + 16) :background-color (if blur? colors/white-opa-5 (colors/theme-colors colors/neutral-5 colors/neutral-90 theme)) - :border-radius (if (= size :default) 6 3) - :margin-vertical (if (= size :default) 4 3)}) + :border-radius (case size + :large 6 + :small 3 + 6) + :margin-vertical (case size + :large 4 + :small 3 + 4)}) (def subtitle-container {:flex-direction :row diff --git a/src/status_im/contexts/preview/quo/settings/data_item.cljs b/src/status_im/contexts/preview/quo/settings/data_item.cljs index 27bfdd2c7a..36d171dd61 100644 --- a/src/status_im/contexts/preview/quo/settings/data_item.cljs +++ b/src/status_im/contexts/preview/quo/settings/data_item.cljs @@ -55,7 +55,8 @@ {:type :select :key :size :options [{:key :default} - {:key :small}]}]) + {:key :small} + {:key :large}]}]) (defn view [] diff --git a/src/status_im/contexts/wallet/wallet_connect/core.cljs b/src/status_im/contexts/wallet/wallet_connect/core.cljs index e48f1fd4d7..5354f335dd 100644 --- a/src/status_im/contexts/wallet/wallet_connect/core.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/core.cljs @@ -45,6 +45,10 @@ [db] (get-in db [:wallet-connect/current-request :event])) +(defn get-session-dapp-metadata + [proposal] + (get-in proposal [:params :proposer :metadata])) + (defn get-db-current-request-params [db] (-> db @@ -61,3 +65,32 @@ (->> password security/safe-unmask-data (f data address)))) + +(defn get-proposal-networks + [proposal] + (let [required-namespaces (get-in proposal [:params :requiredNamespaces]) + optional-namespaces (get-in proposal [:params :optionalNamespaces])] + (->> [required-namespaces optional-namespaces] + (map #(get-in % [:eip155 :chains])) + (apply concat) + (into #{})))) + +(defn proposal-networks-intersection + [proposal supported-networks] + (let [proposed-networks (get-proposal-networks proposal)] + (->> supported-networks + (filter #(->> % + chain-id->eip155 + (contains? proposed-networks)))))) + +(defn required-networks-supported? + [proposal supported-networks] + (let [required-networks (get-in proposal [:params :requiredNamespaces :eip155 :chains]) + supported-eip155 (set (map chain-id->eip155 supported-networks))] + (every? #(contains? supported-eip155 %) required-networks))) + +(defn get-networks-by-mode + [db] + (let [test-mode? (get-in db [:profile/profile :test-networks-enabled?]) + networks (get-in db [:wallet :networks (if test-mode? :test :prod)])] + (mapv #(-> % :chain-id) networks))) diff --git a/src/status_im/contexts/wallet/wallet_connect/events.cljs b/src/status_im/contexts/wallet/wallet_connect/events.cljs index eff08d1ca1..d50838ba5d 100644 --- a/src/status_im/contexts/wallet/wallet_connect/events.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/events.cljs @@ -8,7 +8,6 @@ status-im.contexts.wallet.wallet-connect.responding-events [status-im.contexts.wallet.wallet-connect.utils :as wc-utils] [taoensso.timbre :as log] - [utils.ethereum.chain :as chain] [utils.i18n :as i18n])) (rf/reg-event-fx @@ -53,22 +52,37 @@ (rf/reg-event-fx :wallet-connect/on-session-proposal (fn [{:keys [db]} [proposal]] - (let [accounts (get-in db [:wallet :accounts]) - without-watched (remove :watch-only? (vals accounts))] - (log/info "Received Wallet Connect session proposal: " {:id (:id proposal)}) - {:db (assoc db - ;; NOTE: for now using the first account, but should be using the account selected - ;; by the user on the connection screen. The default would depend on where the - ;; connection started from: - ;; - global scanner -> first account in list - ;; - wallet account dapps -> account that is selected - :wallet-connect/current-proposal - {:request proposal - :address (-> without-watched - first - :address)}) - :fx [[:dispatch - [:open-modal :screen/wallet.wallet-connect-session-proposal]]]}))) + (log/info "Received Wallet Connect session proposal: " {:id (:id proposal)}) + (let [accounts (get-in db [:wallet :accounts]) + without-watched (remove :watch-only? (vals accounts)) + networks (wallet-connect-core/get-networks-by-mode db) + session-networks (wallet-connect-core/proposal-networks-intersection proposal + networks) + required-networks-supported? (wallet-connect-core/required-networks-supported? proposal + networks)] + (if required-networks-supported? + {:db (update db + :wallet-connect/current-proposal assoc + :request proposal + :session-networks session-networks + :address (-> without-watched + first + :address) + ) + :fx [[:dispatch + [:open-modal :screen/wallet.wallet-connect-session-proposal]]]} + {:fx [[:dispatch + [:wallet-connect/session-networks-unsupported proposal]]]})))) + +(rf/reg-event-fx + :wallet-connect/session-networks-unsupported + (fn [_ [proposal]] + (let [{:keys [name]} (wallet-connect-core/get-session-dapp-metadata proposal)] + {:fx [[:dispatch + [:toasts/upsert + {:type :negative + :theme :dark + :text (i18n/label :t/wallet-connect-networks-not-supported {:dapp name})}]]]}))) (rf/reg-event-fx :wallet-connect/on-session-request @@ -136,34 +150,33 @@ (rf/reg-event-fx :wallet-connect/approve-session (fn [{:keys [db]}] - (let [web3-wallet (get db :wallet-connect/web3-wallet) - current-proposal-request (get-in db [:wallet-connect/current-proposal :request]) - supported-chain-ids (->> db - chain/chain-ids - (map wallet-connect-core/chain-id->eip155) - vec) - current-address (get-in db [:wallet-connect/current-proposal :address]) - accounts (-> (partial wallet-connect-core/format-eip155-address current-address) - (map supported-chain-ids)) - supported-namespaces (clj->js {:eip155 - {:chains supported-chain-ids - :methods constants/wallet-connect-supported-methods - :events constants/wallet-connect-supported-events - :accounts accounts}})] + (let [web3-wallet (get db :wallet-connect/web3-wallet) + current-proposal (get-in db [:wallet-connect/current-proposal :request]) + session-networks (->> (get-in db [:wallet-connect/current-proposal :session-networks]) + (map wallet-connect-core/chain-id->eip155) + vec) + current-address (get-in db [:wallet-connect/current-proposal :address]) + accounts (-> (partial wallet-connect-core/format-eip155-address current-address) + (map session-networks)) + supported-namespaces (clj->js {:eip155 + {:chains session-networks + :methods constants/wallet-connect-supported-methods + :events constants/wallet-connect-supported-events + :accounts accounts}})] {:fx [[:effects.wallet-connect/approve-session {:web3-wallet web3-wallet - :proposal current-proposal-request + :proposal current-proposal :supported-namespaces supported-namespaces :on-success (fn [] (log/info "Wallet Connect session approved") - (let [metadata - (-> current-proposal-request :params :proposer :metadata)] + (let [metadata (wallet-connect-core/get-session-dapp-metadata + current-proposal)] (rf/dispatch [:wallet-connect/reset-current-session-proposal]) (rf/dispatch [:wallet-connect/persist-session - {:id (:id current-proposal-request) + {:id (:id current-proposal) :dapp-name (:name metadata) :dapp-url (:url metadata) - :session-info current-proposal-request}]))) + :session-info current-proposal}]))) :on-fail (fn [error] (log/error "Wallet Connect session approval failed" {:error error diff --git a/src/status_im/contexts/wallet/wallet_connect/session_proposal/view.cljs b/src/status_im/contexts/wallet/wallet_connect/session_proposal/view.cljs index 2ae147219e..1062d154e6 100644 --- a/src/status_im/contexts/wallet/wallet_connect/session_proposal/view.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/session_proposal/view.cljs @@ -1,8 +1,8 @@ (ns status-im.contexts.wallet.wallet-connect.session-proposal.view (:require + [clojure.string :as string] [quo.core :as quo] [quo.foundations.colors :as colors] - [quo.foundations.resources :as quo.resources] [quo.theme] [react-native.core :as rn] [status-im.common.floating-button-page.view :as floating-button-page] @@ -45,11 +45,9 @@ [quo/text label]]) labels)])) -(defn- get-placeholder-networks - [] - [{:source (quo.resources/get-network :ethereum)} - {:source (quo.resources/get-network :optimism)} - {:source (quo.resources/get-network :arbitrum)}]) +(defn- format-network-name + [network] + (-> network :network-name name string/capitalize)) (defn- set-current-proposal-address [acc] @@ -93,37 +91,41 @@ (defn- connection-category [] - (let [address (rf/sub [:wallet-connect/current-proposal-address]) - {:keys - [name - customization-color - emoji]} (rf/sub [:wallet-connect/account-details-by-address address]) - data-item-common-props {:blur? false - :description :default - :card? false - :label :preview - :status :default - :size :default} - account-data-item-props (assoc data-item-common-props - :right-content {:type :accounts - :size :size-32 - :data [{:emoji emoji - :customization-color - customization-color}]} - :on-press show-account-switcher-bottom-sheet - :title (i18n/label :t/account-title) - :subtitle name - :icon-right? true - :right-icon :i/chevron-right - :icon-color colors/neutral-10) - networks-data-item-props (assoc data-item-common-props - :right-content {:type :network - :data - (get-placeholder-networks)} - :title (i18n/label :t/networks) - ;; TODO. The quo component for data-item - ;; does not support showing networks yet - :subtitle "Networks placeholder")] + (let [{:keys [session-networks + all-networks-in-session?]} (rf/sub + [:wallet-connect/session-proposal-network-details]) + address (rf/sub [:wallet-connect/current-proposal-address]) + {:keys [name customization-color emoji]} (rf/sub [:wallet-connect/account-details-by-address + address]) + network-names (->> session-networks + (map format-network-name) + (string/join ", ")) + network-images (mapv :source session-networks) + data-item-common-props {:blur? false + :description :default + :card? false + :label :preview + :status :default + :size :large} + account-data-item-props (assoc data-item-common-props + :right-content {:type :accounts + :size :size-32 + :data [{:emoji emoji + :customization-color + customization-color}]} + :on-press show-account-switcher-bottom-sheet + :title (i18n/label :t/account-title) + :subtitle name + :icon-right? true + :right-icon :i/chevron-right + :icon-color colors/neutral-10) + networks-data-item-props (assoc data-item-common-props + :right-content {:type :network + :data network-images} + :title (i18n/label :t/networks) + :subtitle (if all-networks-in-session? + (i18n/label :t/all-networks) + network-names))] [quo/category {:blur? false :list-type :data-item diff --git a/src/status_im/subs/wallet/wallet_connect.cljs b/src/status_im/subs/wallet/wallet_connect.cljs index d452036344..752e8ced79 100644 --- a/src/status_im/subs/wallet/wallet_connect.cljs +++ b/src/status_im/subs/wallet/wallet_connect.cljs @@ -108,10 +108,20 @@ :else nil)}))))) (rf/reg-sub - :wallet-connect/session-proposer + :wallet-connect/current-proposal-request :<- [:wallet-connect/current-proposal] + :-> :request) + +(rf/reg-sub + :wallet-connect/session-proposal-networks + :<- [:wallet-connect/current-proposal] + :-> :session-networks) + +(rf/reg-sub + :wallet-connect/session-proposer + :<- [:wallet-connect/current-proposal-request] (fn [proposal] - (-> proposal :request :params :proposer))) + (-> proposal :params :proposer))) (rf/reg-sub :wallet-connect/session-proposer-name @@ -119,6 +129,18 @@ (fn [proposer] (-> proposer :metadata :name))) +(rf/reg-sub + :wallet-connect/session-proposal-network-details + :<- [:wallet-connect/session-proposal-networks] + :<- [:wallet/network-details] + (fn [[session-networks network-details]] + (let [supported-networks (map :chain-id network-details) + session-networks (filterv #(contains? (set session-networks) (:chain-id %)) + network-details) + all-networks-in-session? (= (count supported-networks) (count session-networks))] + {:session-networks session-networks + :all-networks-in-session? all-networks-in-session?}))) + (rf/reg-sub :wallet-connect/current-proposal-address (fn [db] diff --git a/src/status_im/subs/wallet/wallet_connect_test.cljs b/src/status_im/subs/wallet/wallet_connect_test.cljs index 634ea20e7f..ac39debacb 100644 --- a/src/status_im/subs/wallet/wallet_connect_test.cljs +++ b/src/status_im/subs/wallet/wallet_connect_test.cljs @@ -8,38 +8,39 @@ [utils.re-frame :as rf])) (def sample-session - {:id 1716798889093634 - :params - {:id 1716798889093634 - :pairingTopic "9b18e1348817a548bbc97f9b4a09278f4fdf7c984e4a61ddf461bd1f57710d33" - :expiryTimestamp 1716799189 - :requiredNamespaces {} - :optionalNamespaces {:eip155 - {:chains ["eip155:1" "eip155:42161" "eip155:137" "eip155:43114" "eip155:56" - "eip155:10" "eip155:100" - "eip155:324" "eip155:7777777" "eip155:8453" "eip155:42220" - "eip155:1313161554" "eip155:11155111" "eip155:11155420"] - :methods ["personal_sign" "eth_accounts" "eth_requestAccounts" - "eth_sendRawTransaction" "eth_sendTransaction" - "eth_sign" "eth_signTransaction" "eth_signTypedData" - "eth_signTypedData_v3" "eth_signTypedData_v4" - "wallet_addEthereumChain" "wallet_getCallsStatus" - "wallet_getCapabilities" "wallet_getPermissions" - "wallet_registerOnboarding" "wallet_requestPermissions" - "wallet_scanQRCode" "wallet_sendCalls" - "wallet_showCallsStatus" "wallet_switchEthereumChain" - "wallet_watchAsset"] - :events ["chainChanged" "accountsChanged"]}} - :relays [{:protocol "irn"}] - :proposer {:publicKey "cddea055b8974d93380e6c7e72110145506c06524047866f8034f3db0990137a" - :metadata {:name "Web3Modal" - :description "Web3Modal Laboratory" - :url "https://lab.web3modal.com" - :icons ["https://avatars.githubusercontent.com/u/37784886"]}}} - :verifyContext {:verified {:verifyUrl "https://verify.walletconnect.com" - :validation "VALID" - :origin "https://lab.web3modal.com" - :isScam false}}}) + {:session-proposal + {:id 1716798889093634 + :params + {:id 1716798889093634 + :pairingTopic "9b18e1348817a548bbc97f9b4a09278f4fdf7c984e4a61ddf461bd1f57710d33" + :expiryTimestamp 1716799189 + :requiredNamespaces {} + :optionalNamespaces {:eip155 + {:chains ["eip155:1" "eip155:42161" "eip155:137" "eip155:43114" "eip155:56" + "eip155:10" "eip155:100" + "eip155:324" "eip155:7777777" "eip155:8453" "eip155:42220" + "eip155:1313161554" "eip155:11155111" "eip155:11155420"] + :methods ["personal_sign" "eth_accounts" "eth_requestAccounts" + "eth_sendRawTransaction" "eth_sendTransaction" + "eth_sign" "eth_signTransaction" "eth_signTypedData" + "eth_signTypedData_v3" "eth_signTypedData_v4" + "wallet_addEthereumChain" "wallet_getCallsStatus" + "wallet_getCapabilities" "wallet_getPermissions" + "wallet_registerOnboarding" "wallet_requestPermissions" + "wallet_scanQRCode" "wallet_sendCalls" + "wallet_showCallsStatus" "wallet_switchEthereumChain" + "wallet_watchAsset"] + :events ["chainChanged" "accountsChanged"]}} + :relays [{:protocol "irn"}] + :proposer {:publicKey "cddea055b8974d93380e6c7e72110145506c06524047866f8034f3db0990137a" + :metadata {:name "Web3Modal" + :description "Web3Modal Laboratory" + :url "https://lab.web3modal.com" + :icons ["https://avatars.githubusercontent.com/u/37784886"]}}} + :verifyContext {:verified {:verifyUrl "https://verify.walletconnect.com" + :validation "VALID" + :origin "https://lab.web3modal.com" + :isScam false}}}}) (h/deftest-sub :wallet-connect/session-proposer [sub-name] @@ -54,6 +55,7 @@ (-> sample-session :params :proposer :publicKey))) (is (= (-> proposer :metadata :url) + (-> sample-session :params :proposer :metadata :url)))))) (h/deftest-sub :wallet-connect/session-proposer-name diff --git a/translations/en.json b/translations/en.json index 44f570b184..77d142bdbf 100644 --- a/translations/en.json +++ b/translations/en.json @@ -214,6 +214,7 @@ "airdrop-addresses": "Address for airdrops", "join-as": "Join as {{role}}", "all-addresses": "All addresses", + "all-networks": "All networks", "for-airdrops": "For airdrops", "members-label": "Members", "open-membership": "Open membership", @@ -2043,6 +2044,7 @@ "wallet-connect-sign-message-warning": "Sign messages only if you trust the dApp", "wallet-connect-send-transaction-warning": "Send transactions only if you trust the dApp", "wallet-connect-sign-transaction-warning": "Sign transactions only if you trust the dApp", + "wallet-connect-networks-not-supported": "{{dapp}} requires an unsupported network.", "reject": "Reject", "manage-connections": "Manage connections from within Application Connections", "contact-request-was-ignored": "Contact request ignored",