Skip to content

Commit 4a8bb70

Browse files
[#20150] feat: wallet connect pairing via deep links (#21050)
* [#20150] feat: wallet connect pairing via deep links * [#20150] feat: redirect to the dapp after finish response * [#20150] test: add tests for events and utils * [#20150] fix: reviewer's feedback
1 parent e61702c commit 4a8bb70

File tree

9 files changed

+141
-27
lines changed

9 files changed

+141
-27
lines changed

src/status_im/common/router.cljs

+29-5
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@
3838
[path]
3939
(map #(str % path) status-web-urls))
4040

41-
4241
(def handled-schemes (set (into uri-schemes status-web-urls)))
4342

43+
(defn wc-normalize-uri
44+
[uri]
45+
(if (string/includes? uri "//wc:") (string/replace-first uri "/?" "?") uri))
46+
4447
(def group-chat-extractor
4548
{[#"(.*)" :params] {"" :group-chat
4649
"/" :group-chat}})
@@ -60,7 +63,9 @@
6063
["wallet/" :account] :wallet-account
6164
[user-with-data-path :user-data] :user
6265
"c" :community
63-
"u" :user}
66+
"u" :user
67+
#"wc:([^\?]+)" :wallet-connect
68+
"wc" :wallet-connect}
6469
ethereum-scheme eip-extractor}])
6570

6671
(defn parse-query-params
@@ -75,6 +80,11 @@
7580
(when-not (string/blank? fragment)
7681
fragment)))
7782

83+
(defn remove-scheme
84+
[uri]
85+
(let [[_ url] (string/split uri #"://")]
86+
url))
87+
7888
(defn match-uri
7989
[uri]
8090
(let [;; bidi has trouble parse path with `=` in it extract `=` here and add back to parsed
@@ -88,13 +98,17 @@
8898
uri-without-equal-in-path
8999
(if equal-end-of-base64url (string/replace-first uri equal-end-of-base64url "") uri)
90100

101+
;;bidi has issue to detect wc regex with /?, couldn't fine any other workaround
102+
normalize-uri
103+
(wc-normalize-uri uri-without-equal-in-path)
104+
91105
;; fragment is the one after `#`, usually user-id, ens-name, community-id
92106
fragment (parse-fragment uri)
93107
ens? (utils.ens/is-valid-eth-name? fragment)
94108
compressed-key? (validators/valid-compressed-key? fragment)
95109

96-
{:keys [handler route-params] :as parsed}
97-
(assoc (bidi/match-route routes uri-without-equal-in-path)
110+
{:keys [handler route-params query-params] :as parsed}
111+
(assoc (bidi/match-route routes normalize-uri)
98112
:uri uri
99113
:query-params (parse-query-params uri))]
100114
(cond-> parsed
@@ -131,7 +145,13 @@
131145
(update-in [:route-params :user-data] #(str % equal-end-of-base64url))
132146

133147
(and (= handler :user) compressed-key?)
134-
(assoc-in [:route-params :user-id] fragment))))
148+
(assoc-in [:route-params :user-id] fragment)
149+
150+
(= handler :wallet-connect)
151+
(assoc-in [:route-params :uri]
152+
(if (get query-params "uri")
153+
(get query-params "uri")
154+
(remove-scheme normalize-uri))))))
135155

136156
(defn match-contact-async
137157
[chain {:keys [user-id ens-name]} callback]
@@ -328,6 +348,10 @@
328348
(= handler :wallet-account)
329349
(cb (match-wallet-account route-params))
330350

351+
(= handler :wallet-connect)
352+
(cb {:type :wallet-connect
353+
:uri (:uri route-params)})
354+
331355
(address/address? uri)
332356
(cb (address->eip681 uri))
333357

src/status_im/common/universal_links.cljs

+8-1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@
105105
[full-url]
106106
(log/info "universal-links: no handler for " full-url))
107107

108+
(rf/defn handle-wallet-connect
109+
[_ uri]
110+
(log/info "universal-links: handle-wallet-connect" uri)
111+
{:dispatch-later {:ms 1500
112+
:dispatch [:wallet-connect/on-scan-connection uri]}})
113+
108114
(defn dispatch-url
109115
"Dispatch url so we can get access to re-frame/db"
110116
[url]
@@ -117,7 +123,7 @@
117123

118124
(rf/defn on-handle
119125
{:events [:universal-links/match-value]}
120-
[cofx url {:keys [type chat-id community-id] :as data}]
126+
[cofx url {:keys [type chat-id community-id uri] :as data}]
121127
(case type
122128
:group-chat (handle-group-chat cofx data)
123129
:private-chat (handle-private-chat cofx data)
@@ -130,6 +136,7 @@
130136
:browser (handle-browse cofx data)
131137
:eip681 (handle-eip681 cofx data)
132138
:wallet-account (handle-wallet-account cofx data)
139+
:wallet-connect (handle-wallet-connect cofx uri)
133140
(handle-not-found url)))
134141

135142
(rf/defn route-url

src/status_im/contexts/wallet/wallet_connect/core.cljs

+13
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@
5252
[proposal]
5353
(get-in proposal [:params :proposer :metadata]))
5454

55+
(defn get-current-request-dapp
56+
[request sessions]
57+
(let [dapp-url (get-in request [:event :verifyContext :verified :origin])]
58+
(->> sessions
59+
(filter (fn [session]
60+
(= (utils.string/remove-trailing-slash dapp-url)
61+
(utils.string/remove-trailing-slash (get session :url)))))
62+
first)))
63+
64+
(defn get-dapp-redirect-url
65+
[session]
66+
(get-in session [:peer :metadata :redirect :native]))
67+
5568
(defn get-db-current-request-params
5669
[db]
5770
(-> db
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
(ns status-im.contexts.wallet.wallet-connect.core-test
2+
(:require
3+
[cljs.test :refer-macros [deftest is testing]]
4+
[status-im.contexts.wallet.wallet-connect.core :as sut]))
5+
6+
(deftest get-current-request-dapp-test
7+
(testing "returns the correct dapp based on the request's origin"
8+
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
9+
sessions [{:url "https://dapp.com"}
10+
{:url "https://anotherdapp.com"}]]
11+
(is (= {:url "https://dapp.com"}
12+
(sut/get-current-request-dapp request sessions)))))
13+
14+
(testing "returns nil if no matching dapp is found"
15+
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
16+
sessions [{:url "https://anotherdapp.com"}]]
17+
(is (nil? (sut/get-current-request-dapp request sessions))))))
18+
19+
(deftest get-dapp-redirect-url-test
20+
(testing "returns the native redirect URL if it exists"
21+
(let [session {:peer {:metadata {:redirect {:native "native://redirect-url"}}}}]
22+
(is (= "native://redirect-url"
23+
(sut/get-dapp-redirect-url session)))))
24+
25+
(testing "returns nil if no redirect URL is found"
26+
(let [session {:peer {:metadata {}}}]
27+
(is (nil? (sut/get-dapp-redirect-url session))))))

src/status_im/contexts/wallet/wallet_connect/events.cljs

+11-7
Original file line numberDiff line numberDiff line change
@@ -317,13 +317,17 @@
317317
(rf/reg-event-fx
318318
:wallet-connect/persist-session
319319
(fn [_ [session-info]]
320-
{:fx [[:json-rpc/call
321-
[{:method "wallet_addWalletConnectSession"
322-
:params [(js/JSON.stringify session-info)]
323-
:on-success (fn []
324-
(log/info "Wallet Connect session persisted")
325-
(rf/dispatch [:wallet-connect/fetch-persisted-sessions]))
326-
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]}))
320+
(let [redirect-url (-> session-info
321+
(js->clj :keywordize-keys true)
322+
(wallet-connect-core/get-dapp-redirect-url))]
323+
{:fx [[:json-rpc/call
324+
[{:method "wallet_addWalletConnectSession"
325+
:params [(js/JSON.stringify session-info)]
326+
:on-success (fn []
327+
(log/info "Wallet Connect session persisted")
328+
(rf/dispatch [:wallet-connect/fetch-persisted-sessions])
329+
(rf/dispatch [:wallet-connect/redirect-to-dapp redirect-url]))
330+
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]})))
327331

328332
(rf/reg-event-fx
329333
:wallet-connect/disconnect-session

src/status_im/contexts/wallet/wallet_connect/responding_events.cljs

+15-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
66
[status-im.contexts.wallet.wallet-connect.utils :as wc-utils]
77
[taoensso.timbre :as log]
8-
[utils.i18n :as i18n]))
8+
[utils.i18n :as i18n]
9+
[utils.transforms :as transforms]))
910

1011
(rf/reg-event-fx
1112
:wallet-connect/respond-current-session
@@ -132,8 +133,21 @@
132133
:event :wallet-connect/send-response
133134
:wallet-connect-event event}))
134135
:on-success (fn []
136+
(rf/dispatch [:wallet-connect/redirect-to-dapp])
135137
(log/info "Successfully sent Wallet Connect response to dApp"))}]]}))))
136138

139+
(rf/reg-event-fx
140+
:wallet-connect/redirect-to-dapp
141+
(fn [{:keys [db]} [url]]
142+
(let [redirect-url (or url
143+
(->> (get db :wallet-connect/current-request)
144+
(wallet-connect-core/get-current-request-dapp
145+
(get db :wallet-connect/sessions))
146+
:sessionJson
147+
transforms/json->clj
148+
wallet-connect-core/get-dapp-redirect-url))]
149+
{:fx [[:open-url redirect-url]]})))
150+
137151
(rf/reg-event-fx
138152
:wallet-connect/dismiss-request-modal
139153
(fn [{:keys [db]} _]
@@ -143,12 +157,6 @@
143157
wallet-connect-core/method-to-screen)]
144158
{:fx [[:dispatch [:dismiss-modal screen]]]})))
145159

146-
(rf/reg-event-fx
147-
:wallet-connect/finish-session-request
148-
(fn [_ [result]]
149-
{:fx [[:dispatch [:wallet-connect/send-response {:result result}]]
150-
[:dispatch [:wallet-connect/dismiss-request-modal]]]}))
151-
152160
(rf/reg-event-fx
153161
:wallet-connect/dismiss-request-modal
154162
(fn [{:keys [db]} _]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
(ns status-im.contexts.wallet.wallet-connect.responding-events-test
2+
(:require
3+
[cljs.test :refer-macros [is]]
4+
matcher-combinators.test
5+
[re-frame.db :as rf-db]
6+
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
7+
status-im.contexts.wallet.wallet-connect.responding-events
8+
[test-helpers.unit :as h]
9+
[utils.transforms :as transforms]))
10+
11+
(h/deftest-event :wallet-connect/redirect-to-dapp
12+
[event-id dispatch]
13+
(let [current-request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
14+
session-json "{\"peer\":{\"metadata\":{\"redirect\":{\"native\":\"native://redirect-url\"}}}}"
15+
sessions [{:url "https://dapp.com"
16+
:sessionJson session-json}]]
17+
(reset! rf-db/app-db {:wallet-connect {:current-request current-request
18+
:sessions sessions}})
19+
(with-redefs [wallet-connect-core/get-current-request-dapp
20+
(fn [_ _] (first sessions))
21+
transforms/json->clj
22+
(fn [json] (js/JSON.parse json))
23+
wallet-connect-core/get-dapp-redirect-url
24+
(fn [_] "native://redirect-url")]
25+
26+
(is (match? {:fx [[:open-url "native://redirect-url"]]}
27+
(dispatch [event-id]))))))

src/status_im/navigation/events.cljs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
(ns status-im.navigation.events
22
(:require
3+
[clojure.string :as string]
34
[re-frame.core :as re-frame]
5+
[react-native.core :as react]
46
[status-im.contexts.shell.jump-to.events :as shell.events]
57
[status-im.contexts.shell.jump-to.state :as shell.state]
68
[status-im.contexts.shell.jump-to.utils :as shell.utils]
79
[status-im.feature-flags :as ff]
8-
[utils.re-frame :as rf]))
10+
[utils.re-frame :as rf]
11+
[utils.url :as url]))
912

1013
(defn- all-screens-params
1114
[db view screen-params]
@@ -142,3 +145,9 @@
142145
{:fx [[:effects.share/open config]]})
143146

144147
(rf/reg-event-fx :open-share open-share)
148+
149+
(rf/reg-fx
150+
:open-url
151+
(fn [url]
152+
(when (not (string/blank? url))
153+
(.openURL ^js react/linking (url/normalize-url url)))))

src/status_im/subs/wallet/wallet_connect.cljs

+1-6
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,7 @@
4141
:<- [:wallet-connect/current-request]
4242
:<- [:wallet-connect/sessions]
4343
(fn [[request sessions]]
44-
(let [dapp-url (get-in request [:event :verifyContext :verified :origin])]
45-
(->> sessions
46-
(filter (fn [session]
47-
(= (utils.string/remove-trailing-slash dapp-url)
48-
(utils.string/remove-trailing-slash (get session :url)))))
49-
(first)))))
44+
(wallet-connect-core/get-current-request-dapp request sessions)))
5045

5146
(rf/reg-sub
5247
:wallet-connect/sessions-for-current-account

0 commit comments

Comments
 (0)