Fix WalletConnect sessions disappearing (#21350)

* feat: moved disconnect logic to function

* ref: moved rpc calls to rpc ns

* ref: moved session approval logic to function

* fix: small fixes for sessions

* test: wallet-connect/on-session-delete event test

* test: added event tests for wc sessions

* fix: require sessions events ns

* fix: the wallet was loaded after wc sometimes
This commit is contained in:
Lungu Cristian 2024-10-02 18:16:20 +03:00 committed by GitHub
parent 97aff27980
commit d748ccdef9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 347 additions and 158 deletions

View File

@ -102,8 +102,6 @@
[:dispatch-later [{:ms 1500 :dispatch [:profile.login/non-critical-initialization]}]] [:dispatch-later [{:ms 1500 :dispatch [:profile.login/non-critical-initialization]}]]
[:dispatch [:network/check-expensive-connection]] [:dispatch [:network/check-expensive-connection]]
[:profile.settings/get-profile-picture key-uid] [:profile.settings/get-profile-picture key-uid]
(when (ff/enabled? ::ff/wallet.wallet-connect)
[:dispatch [:wallet-connect/init]])
(when notifications-enabled? (when notifications-enabled?
[:effects/push-notifications-enable])]}))) [:effects/push-notifications-enable])]})))

View File

@ -478,7 +478,9 @@
[:dispatch [:wallet/get-ethereum-chains]] [:dispatch [:wallet/get-ethereum-chains]]
[:dispatch [:wallet/get-accounts]] [:dispatch [:wallet/get-accounts]]
[:dispatch [:wallet/get-keypairs]] [:dispatch [:wallet/get-keypairs]]
[:dispatch [:wallet/get-saved-addresses]]]})) [:dispatch [:wallet/get-saved-addresses]]
(when (ff/enabled? ::ff/wallet.wallet-connect)
[:dispatch-later [{:ms 500 :dispatch [:wallet-connect/init]}]])]}))
(rf/reg-event-fx :wallet/share-account (rf/reg-event-fx :wallet/share-account
(fn [_ [{:keys [content title]}]] (fn [_ [{:keys [content title]}]]

View File

@ -21,8 +21,8 @@
{:fx [[:effects.wallet-connect/init {:fx [[:effects.wallet-connect/init
{:on-success #(rf/dispatch [:wallet-connect/on-init-success %]) {:on-success #(rf/dispatch [:wallet-connect/on-init-success %])
:on-fail #(rf/dispatch [:wallet-connect/on-init-fail %])}]]}) :on-fail #(rf/dispatch [:wallet-connect/on-init-fail %])}]]})
;; NOTE: when offline, fetching persistent sessions only ;; NOTE: when offline, fetching persistent sessions without initializing WC
{:fx [[:dispatch [:wallet-connect/fetch-persisted-sessions]]]})))) {:fx [[:dispatch [:wallet-connect/get-sessions]]]}))))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/on-init-success :wallet-connect/on-init-success
@ -30,7 +30,7 @@
(log/info "WalletConnect SDK initialisation successful") (log/info "WalletConnect SDK initialisation successful")
{:db (assoc db :wallet-connect/web3-wallet web3-wallet) {:db (assoc db :wallet-connect/web3-wallet web3-wallet)
:fx [[:dispatch [:wallet-connect/register-event-listeners]] :fx [[:dispatch [:wallet-connect/register-event-listeners]]
[:dispatch [:wallet-connect/fetch-persisted-sessions]]]})) [:dispatch [:wallet-connect/get-sessions]]]}))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/reload-on-network-change :wallet-connect/reload-on-network-change

View File

@ -5,6 +5,7 @@
[react-native.wallet-connect :as wallet-connect] [react-native.wallet-connect :as wallet-connect]
[status-im.config :as config] [status-im.config :as config]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.signing :as signing] [status-im.contexts.wallet.wallet-connect.utils.signing :as signing]
[status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions] [status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data] [status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
@ -42,35 +43,19 @@
(rf/reg-fx (rf/reg-fx
:effects.wallet-connect/disconnect :effects.wallet-connect/disconnect
(fn [{:keys [web3-wallet topic reason on-success on-fail]}] (fn [{:keys [web3-wallet topic on-success on-fail]}]
(-> (wallet-connect/disconnect-session {:web3-wallet web3-wallet (-> (sessions/disconnect web3-wallet topic)
:topic topic
:reason reason})
(promesa/then on-success) (promesa/then on-success)
(promesa/catch on-fail)))) (promesa/catch on-fail))))
(rf/reg-fx (rf/reg-fx
:effects.wallet-connect/approve-session :effects.wallet-connect/approve-session
(fn [{:keys [web3-wallet proposal networks accounts on-success on-fail]}] (fn [{:keys [web3-wallet proposal-request session-networks address on-success on-fail]}]
(let [{:keys [params id]} proposal (-> (sessions/approve
approved-namespaces (->> {:eip155 {:web3-wallet web3-wallet
{:chains networks :proposal-request proposal-request
:accounts accounts :address address
:methods constants/wallet-connect-supported-methods :session-networks session-networks})
:events constants/wallet-connect-supported-events}}
(wallet-connect/build-approved-namespaces
params))]
(-> (wallet-connect/approve-session
{:web3-wallet web3-wallet
:id id
:approved-namespaces approved-namespaces})
(promesa/then on-success)
(promesa/catch on-fail)))))
(rf/reg-fx
:effects.wallet-connect/fetch-active-sessions
(fn [{:keys [web3-wallet on-success on-fail]}]
(-> (wallet-connect/get-active-sessions web3-wallet)
(promesa/then on-success) (promesa/then on-success)
(promesa/catch on-fail)))) (promesa/catch on-fail))))
@ -156,3 +141,10 @@
:reason reason}) :reason reason})
(promesa/then on-success) (promesa/then on-success)
(promesa/catch on-error))))) (promesa/catch on-error)))))
(rf/reg-fx
:effects.wallet-connect/get-sessions
(fn [{:keys [web3-wallet addresses online? on-success on-error]}]
(-> (sessions/get-sessions web3-wallet addresses online?)
(promesa/then on-success)
(promesa/catch on-error))))

View File

@ -109,7 +109,6 @@
(fn [{:keys [db]} [address]] (fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet-connect/current-proposal :address] address)})) {:db (assoc-in db [:wallet-connect/current-proposal :address] address)}))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/approve-session :wallet-connect/approve-session
(fn [{:keys [db]}] (fn [{:keys [db]}]
@ -119,33 +118,51 @@
(map networks/chain-id->eip155) (map networks/chain-id->eip155)
vec) vec)
current-address (get-in db [:wallet-connect/current-proposal :address]) current-address (get-in db [:wallet-connect/current-proposal :address])
accounts (-> (partial networks/format-eip155-address current-address)
(map session-networks))
network-status (:network/status db) network-status (:network/status db)
expiry (get-in current-proposal [:params :expiryTimestamp])] expired? (-> current-proposal
(get-in [:params :expiryTimestamp])
uri/timestamp-expired?)]
(if (= network-status :online) (if (= network-status :online)
{:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true) {:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true)
:fx [(if (uri/timestamp-expired? expiry) :fx [(if expired?
[:dispatch [:dispatch
[:toasts/upsert [:toasts/upsert
{:id :wallet-connect-proposal-expired {:id :wallet-connect-proposal-expired
:type :negative :type :negative
:text (i18n/label :t/wallet-connect-proposal-expired)}]] :text (i18n/label :t/wallet-connect-proposal-expired)}]]
[:effects.wallet-connect/approve-session [:effects.wallet-connect/approve-session
{:web3-wallet web3-wallet {:web3-wallet web3-wallet
:proposal current-proposal :proposal-request current-proposal
:networks session-networks :session-networks session-networks
:accounts accounts :address current-address
:on-success (fn [approved-session] :on-success #(rf/dispatch [:wallet-connect/approve-session-success %])
(log/info "Wallet Connect session approved") :on-fail #(rf/dispatch [:wallet-connect/approve-session-error %])}])
(rf/dispatch [:wallet-connect/reset-current-session-proposal])
(rf/dispatch [:wallet-connect/persist-session
approved-session]))
:on-fail (fn [error]
(log/error "Wallet Connect session approval failed"
{:error error
:event :wallet-connect/approve-session})
(rf/dispatch
[:wallet-connect/reset-current-session-proposal]))}])
[:dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]]]} [:dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]]]}
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]})))) {:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
(rf/reg-event-fx :wallet-connect/approve-session-success
(fn [_ [session]]
(log/info "Wallet Connect session approved")
{:fx [[:dispatch [:wallet-connect/on-new-session session]]
[:dispatch [:wallet-connect/reset-current-session-proposal]]
[:dispatch [:wallet-connect/redirect-to-dapp (data-store/get-dapp-redirect-url session)]]]}))
(rf/reg-event-fx :wallet-connect/approve-session-error
(fn [_ [error]]
(log/error "Wallet Connect session approval failed"
{:error error
:event :wallet-connect/approve-session})
{:fx [[:dispatch [:wallet-connect/reset-current-session-proposal]]]}))
(rf/reg-event-fx
:wallet-connect/reject-session-proposal
(fn [{:keys [db]} [proposal]]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)]
{:fx [(when-not response-sent?
[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal (or proposal request)
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}])
[:dispatch [:wallet-connect/reset-current-session-proposal]]]})))

View File

@ -165,19 +165,6 @@
{:fx [[:dispatch [:wallet-connect/send-response {:result result}]] {:fx [[:dispatch [:wallet-connect/send-response {:result result}]]
[:dispatch [:wallet-connect/dismiss-request-modal]]]})) [:dispatch [:wallet-connect/dismiss-request-modal]]]}))
(rf/reg-event-fx
:wallet-connect/reject-session-proposal
(fn [{:keys [db]} [proposal]]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)]
{:fx [(when-not response-sent?
[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal (or proposal request)
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}])
[:dispatch [:wallet-connect/reset-current-session-proposal]]]})))
;; NOTE: Currently we only reject a session if the user dismissed a modal ;; NOTE: Currently we only reject a session if the user dismissed a modal
;; without accepting the session first. ;; without accepting the session first.
;; But this needs to be solidified to ensure other cases: ;; But this needs to be solidified to ensure other cases:

View File

@ -1,20 +1,20 @@
(ns status-im.contexts.wallet.wallet-connect.events.sessions (ns status-im.contexts.wallet.wallet-connect.events.sessions
(:require [re-frame.core :as rf] (:require [re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks] [status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions] [status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.transforms :as types])) [utils.i18n :as i18n]))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/on-session-delete :wallet-connect/on-session-delete
(fn [{:keys [db]} [{:keys [topic] :as event}]] (fn [{:keys [db]} [{:keys [topic] :as event}]]
(when (networks/event-should-be-handled? db event) (when (networks/event-should-be-handled? db event)
(log/info "Received Wallet Connect session delete from the SDK: " event) (log/info "Received Wallet Connect session delete from the SDK: " event)
{:fx [[:dispatch [:wallet-connect/disconnect-persisted-session topic]]]}))) {:fx [[:json-rpc/call
[{:method "wallet_disconnectWalletConnectSession"
:params [topic]
:on-success [:wallet-connect/delete-session topic]
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]})))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/disconnect-dapp :wallet-connect/disconnect-dapp
@ -26,116 +26,64 @@
{:fx [[:effects.wallet-connect/disconnect {:fx [[:effects.wallet-connect/disconnect
{:web3-wallet web3-wallet {:web3-wallet web3-wallet
:topic topic :topic topic
:reason (wallet-connect/get-sdk-error
constants/wallet-connect-user-disconnected-reason-key)
:on-fail on-fail :on-fail on-fail
:on-success (fn [] :on-success (fn []
(rf/dispatch [:wallet-connect/disconnect-persisted-session topic]) (log/info "Successfully disconnected dApp session" topic)
(rf/dispatch [:wallet-connect/delete-session topic])
(when on-success (when on-success
(on-success)))}]]} (on-success)))}]]}
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]})))) {:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
;; We first load sessions from database, then we initiate a call to Wallet Connect SDK and
;; then replace the list we have stored in the database with the one that came from the SDK.
;; In addition to that, we also update the backend state by marking sessions that are not
;; active anymore by calling `:wallet-connect/disconnect-session`.
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/fetch-active-sessions-success :wallet-connect/get-sessions
(fn [{:keys [db now]} [sessions]]
(let [persisted-sessions (:wallet-connect/sessions db)
account-addresses (->> (get-in db [:wallet :accounts])
vals
sessions/filter-operable-accounts
(map :address))
sessions (->> (js->clj sessions :keywordize-keys true)
vals
(map sessions/sdk-session->db-session)
(sessions/filter-sessions-for-account-addresses
account-addresses))
session-topics (set (map :topic sessions))
expired-sessions (filter
(fn [{:keys [expiry topic]}]
(or (< expiry (/ now 1000))
(not (contains? session-topics topic))))
persisted-sessions)]
(when (seq expired-sessions)
(log/info "Updating WalletConnect persisted sessions due to expired/inactive sessions"
{:expired expired-sessions}))
{:fx (mapv (fn [{:keys [topic]}]
[:dispatch [:wallet-connect/disconnect-persisted-session topic]])
expired-sessions)
:db (assoc db :wallet-connect/sessions sessions)})))
(rf/reg-event-fx
:wallet-connect/fetch-active-sessions
(fn [{:keys [db]}] (fn [{:keys [db]}]
(let [web3-wallet (get db :wallet-connect/web3-wallet)] (let [addresses (->> (get-in db [:wallet :accounts])
{:fx [[:effects.wallet-connect/fetch-active-sessions vals
{:web3-wallet web3-wallet sessions/filter-operable-accounts
:on-fail #(log/error "Failed to get active sessions" {:error %}) (map :address))]
:on-success #(rf/dispatch [:wallet-connect/fetch-active-sessions-success %])}]]}))) (if (not (seq addresses))
;; NOTE: Re-trying to get active sessions if accounts weren't loaded yet during
;; initialization
((log/info "Re-trying to fetch active WalletConnect sessions")
{:fx [[:dispatch-later [{:ms 500 :dispatch [:wallet-connect/get-sessions]}]]]})
{:fx [[:effects.wallet-connect/get-sessions
{:online? (-> db :network/status (= :online))
:web3-wallet (get db :wallet-connect/web3-wallet)
:addresses addresses
:on-success #(rf/dispatch [:wallet-connect/get-sessions-success %])
:on-error #(rf/dispatch [:wallet-connect/get-sessions-error %])}]]}))))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/fetch-persisted-sessions-success :wallet-connect/get-sessions-success
(fn [{:keys [db]} [sessions]] (fn [{:keys [db]} [sessions]]
(let [network-status (:network/status db) (log/info "WalletConnect sessions loaded successfully")
sessions' (mapv (fn [{:keys [sessionJson] :as session}] {:db (assoc db :wallet-connect/sessions sessions)}))
(assoc session
:accounts
(-> sessionJson
types/json->clj
:namespaces
:eip155
:accounts)))
sessions)]
{:fx [(when (= network-status :online)
[:dispatch [:wallet-connect/fetch-active-sessions]])]
:db (assoc db :wallet-connect/sessions sessions')})))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/fetch-persisted-sessions-fail :wallet-connect/get-sessions-error
(fn [_ [error]] (fn [_ [error]]
(log/info "Wallet Connect fetch persisted sessions failed" error) (log/error "WalletConnect sessions failed to load" error)
{:fx [[:dispatch [:wallet-connect/fetch-active-sessions]]]})) {:fx [[:dispatch
[:toasts/upsert
{:type :negative
:text (i18n/label :t/wallet-connect-connections-error)}]]]}))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/fetch-persisted-sessions :wallet-connect/on-new-session
(fn [{:keys [now]} _] (fn [{:keys [db]} [new-session]]
(let [current-timestamp (quot now 1000)] {:db (update db
{:fx [[:json-rpc/call :wallet-connect/sessions
[{:method "wallet_getWalletConnectActiveSessions" (fn [sessions]
;; NOTE: This is the activeSince timestamp to avoid expired sessions (->> new-session
:params [current-timestamp] sessions/sdk-session->db-session
:on-success [:wallet-connect/fetch-persisted-sessions-success] (conj sessions))))}))
:on-error [:wallet-connect/fetch-persisted-sessions-fail]}]]]})))
(rf/reg-event-fx (rf/reg-event-fx
:wallet-connect/persist-session :wallet-connect/delete-session
(fn [_ [session-info]]
(let [redirect-url (-> session-info
(js->clj :keywordize-keys true)
(data-store/get-dapp-redirect-url))]
{:fx [[:json-rpc/call
[{:method "wallet_addWalletConnectSession"
:params [(js/JSON.stringify session-info)]
:on-success (fn []
(log/info "Wallet Connect session persisted")
(rf/dispatch [:wallet-connect/fetch-persisted-sessions])
(rf/dispatch [:wallet-connect/redirect-to-dapp redirect-url]))
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]})))
(rf/reg-event-fx
:wallet-connect/disconnect-persisted-session
(fn [{:keys [db]} [topic]] (fn [{:keys [db]} [topic]]
(log/info "Removing session from persistance and state" topic)
{:db (update db {:db (update db
:wallet-connect/sessions :wallet-connect/sessions
(fn [sessions] (fn [sessions]
(->> sessions (->> sessions
(remove #(= (:topic %) topic)) (remove #(= (:topic %) topic))
(into [])))) (into []))))}))
:fx [[:json-rpc/call
[{:method "wallet_disconnectWalletConnectSession"
:params [topic]
:on-success #(log/info "Wallet Connect session disconnected")
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]}))

View File

@ -0,0 +1,122 @@
(ns status-im.contexts.wallet.wallet-connect.events.sessions-test
(:require
[cljs.test :refer-macros [is are testing]]
matcher-combinators.test
[re-frame.db :as rf-db]
status-im.contexts.wallet.wallet-connect.events.session-responses
status-im.contexts.wallet.wallet-connect.events.sessions
[test-helpers.unit :as h]))
(defn- find-fx
[fx name]
(some #(when (= name (first %)) %) fx))
(defn- get-fx-arg
"Finds the arg value for an effect, if present in the fx vector"
[fx fx-name arg-fn]
(-> fx
(find-fx fx-name)
second
arg-fn))
(h/deftest-event :wallet-connect/on-session-delete
[event-id dispatch]
(testing "successfully deletes the session"
(reset! rf-db/app-db {:profile/profile {:test-networks-enabled? false}
:wallet-connect/sessions [{:topic "topic"
:chains ["eip155:1"]}]})
(let [fx (:fx (dispatch [event-id
{:topic "topic"
:chains ["eip155:1"]}]))
get-rpc-fx-arg (partial get-fx-arg fx :json-rpc/call)]
(are [expected result] (match? expected result)
:json-rpc/call (-> fx (find-fx :json-rpc/call) first)
"wallet_disconnectWalletConnectSession" (get-rpc-fx-arg (comp :method first))
["topic"] (get-rpc-fx-arg (comp :params first))
[:wallet-connect/delete-session "topic"] (get-rpc-fx-arg (comp :on-success first)))))
(testing "ignore the deletion if session topic not found"
(let [topic-1 "topic-1"
topic-2 "topic-2"]
(reset! rf-db/app-db {:profile/profile {:test-networks-enabled? false}
:wallet-connect/sessions [{:topic topic-1
:chains ["eip155:1"]}]})
(is (match? nil
(dispatch [event-id
{:topic topic-2
:chains ["eip155:1"]}])))))
(testing "ignore the deletion if the event is for the wrong network mode (testnet/mainnet)"
(let [topic "topic"]
(reset! rf-db/app-db {:profile/profile {:test-networks-enabled? true}
:wallet-connect/sessions [{:topic topic
:chains ["eip155:1"]}]})
(is (match? nil
(dispatch [event-id
{:topic topic
:chains ["eip155:11155111"]}]))))))
(h/deftest-event :wallet-connect/disconnect-dapp
[event-id dispatch]
(testing "disconnecting from dApp when online"
(reset! rf-db/app-db {:network/status :online
:wallet-connect/web3-wallet "mock"})
(let [fx (:fx (dispatch [event-id {:topic "topic"}]))]
(are [expected result] (match? expected result)
:effects.wallet-connect/disconnect (ffirst fx)
"topic" (-> fx first second :topic)
"mock" (-> fx first second :web3-wallet))))
(testing "showing no-internet toast when offline"
(reset! rf-db/app-db {:network/status :offline})
(is (match? :wallet-connect/no-internet-toast
(-> (dispatch [event-id {:topic "topic"}])
:fx
first
second
first)))))
(h/deftest-event :wallet-connect/get-sessions
[event-id dispatch]
(testing "the fx includes only for the available accounts"
(reset! rf-db/app-db {:wallet {:accounts {"available" {:address "0x123"
:operable? true}
"watch-only" {:address "0x456"
:operable? true
:watch-only? true}
"non-operable" {:address "0x789"
:operable? false}}}
:network/status :online
:wallet-connect/web3-wallet "mock"})
(let [fx (:fx (dispatch [event-id]))
get-sessions-fx-arg (partial get-fx-arg fx :effects.wallet-connect/get-sessions)]
(are [expected result] (match? expected result)
:effects.wallet-connect/get-sessions (-> fx (find-fx :effects.wallet-connect/get-sessions) first)
true (get-sessions-fx-arg :online?)
'("0x123") (get-sessions-fx-arg :addresses)
"mock" (get-sessions-fx-arg :web3-wallet)))))
(h/deftest-event :wallet-connect/get-sessions-success
[event-id dispatch]
(testing "sessions are stored in the db"
(let [sessions '({:topic "123"} {:topic "456"})]
(is (match? {:db {:wallet-connect/sessions sessions}}
(dispatch [event-id sessions]))))))
(h/deftest-event :wallet-connect/on-new-session
[event-id dispatch]
(testing "new session is added to db"
(let [sessions '({:topic "123"} {:topic "456"})
new-session {:topic "789"}]
(reset! rf-db/app-db {:wallet-connect/sessions sessions})
(is (match? {:db {:wallet-connect/sessions (conj sessions new-session)}}
(dispatch [event-id new-session]))))))
(h/deftest-event :wallet-connect/delete-session
[event-id dispatch]
(testing "session is deleted from db"
(let [sessions '({:topic "123"} {:topic "456"})
expected '({:topic "123"})]
(reset! rf-db/app-db {:wallet-connect/sessions sessions})
(is (match? {:db {:wallet-connect/sessions expected}}
(dispatch [event-id "456"]))))))

View File

@ -58,6 +58,23 @@
(-> (rpc-events/call-async "wallet_getSuggestedFees" true chain-id) (-> (rpc-events/call-async "wallet_getSuggestedFees" true chain-id)
(promesa/then transforms/js->clj))) (promesa/then transforms/js->clj)))
(defn wallet-disconnect-persisted-session
[topic]
(rpc-events/call-async "wallet_disconnectWalletConnectSession" true topic))
(defn wallet-get-persisted-sessions
([]
(let [now (-> (js/Date.) .getTime (quot 1000))]
(wallet-get-persisted-sessions now)))
([expiry-timestamp]
(rpc-events/call-async "wallet_getWalletConnectActiveSessions" false expiry-timestamp)))
(defn wallet-persist-session
[session]
(->> session
transforms/clj->json
(rpc-events/call-async "wallet_addWalletConnectSession" false)))
(defn wallet-get-transaction-estimated-time (defn wallet-get-transaction-estimated-time
[chain-id max-fee-per-gas] [chain-id max-fee-per-gas]
(-> (rpc-events/call-async "wallet_getTransactionEstimatedTime" true chain-id max-fee-per-gas) (-> (rpc-events/call-async "wallet_getTransactionEstimatedTime" true chain-id max-fee-per-gas)

View File

@ -1,6 +1,12 @@
(ns status-im.contexts.wallet.wallet-connect.utils.sessions (ns status-im.contexts.wallet.wallet-connect.utils.sessions
(:require (:require
[clojure.string :as string] [clojure.string :as string]
[promesa.core :as promesa]
[react-native.wallet-connect :as wallet-connect]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[status-im.contexts.wallet.wallet-connect.utils.rpc :as rpc]
[taoensso.timbre :as log]
[utils.transforms :as transforms])) [utils.transforms :as transforms]))
(defn sdk-session->db-session (defn sdk-session->db-session
@ -42,3 +48,102 @@
first first
(string/split #":") (string/split #":")
last))) last)))
(defn- parse-session-accounts
[{:keys [sessionJson] :as session}]
(assoc session
:accounts
(-> sessionJson
:namespaces
:eip155
:accounts)))
(defn- find-inactive-sessions
[active-sessions persisted-sessions]
(->> persisted-sessions
(filter #(->> %
:topic
(contains? (->> active-sessions
(map :topic)
set))
not))))
(defn get-persisted-sessions
[]
(-> (rpc/wallet-get-persisted-sessions)
(promesa/then #(map parse-session-accounts %))
(promesa/catch (fn [err]
(throw (ex-info "Failed to get persisted WalletConnect sessions"
{:error err
:code :error/wc-get-persisted-sessions}))))))
(defn get-active-sessions
[web3-wallet addresses]
(-> (wallet-connect/get-active-sessions web3-wallet)
(promesa/then #(->>
(transforms/js->clj %)
vals
(map sdk-session->db-session)
(filter-sessions-for-account-addresses addresses)))
(promesa/catch (fn [err]
(throw (ex-info "Failed to get active WalletConnect sessions"
{:error err
:code :error/wc-get-active-sessions}))))))
(defn sync-persisted-sessions
[active-sessions persisted-sessions]
(-> (promesa/all
(for [topic (find-inactive-sessions active-sessions
persisted-sessions)]
(do (log/info "Syncing disconnected session with persistance" topic)
(rpc/wallet-disconnect-persisted-session topic))))
(promesa/catch (fn [err]
(throw (ex-info "Failed to synchronize persisted sessions"
{:error err
:code :error/wc-sync-persisted-sessions}))))))
(defn get-sessions
[web3-wallet addresses online?]
(promesa/let [persisted-sessions (get-persisted-sessions)]
(if online?
(promesa/let [active-sessions (get-active-sessions web3-wallet addresses)]
(sync-persisted-sessions active-sessions persisted-sessions)
active-sessions)
persisted-sessions)))
(defn disconnect
[web3-wallet topic]
(let [reason (wallet-connect/get-sdk-error constants/wallet-connect-user-disconnected-reason-key)]
(->
(promesa/do
(wallet-connect/disconnect-session {:web3-wallet web3-wallet
:topic topic
:reason reason})
(rpc/wallet-disconnect-persisted-session topic))
(promesa/catch (fn [err]
(throw (ex-info "Failed to disconnect dapp"
{:err err
:code :error/wc-disconnect-dapp})))))))
(defn approve
[{:keys [web3-wallet address session-networks proposal-request]}]
(let [{:keys [params id]} proposal-request
accounts (-> (partial networks/format-eip155-address address)
(map session-networks))]
(-> (promesa/let [session
(wallet-connect/approve-session
{:web3-wallet web3-wallet
:id id
:approved-namespaces (->>
{:eip155
{:chains session-networks
:accounts accounts
:methods constants/wallet-connect-supported-methods
:events constants/wallet-connect-supported-events}}
(wallet-connect/build-approved-namespaces params))})]
(rpc/wallet-persist-session session)
(transforms/js->clj session))
(promesa/catch (fn [err]
(throw (ex-info "Failed to approve session"
{:err err
:code :error/wc-approve})))))))

View File

@ -2698,6 +2698,7 @@
"wallet-connect": "Wallet Connect", "wallet-connect": "Wallet Connect",
"wallet-connect-2.0": "Wallet Connect 2.0", "wallet-connect-2.0": "Wallet Connect 2.0",
"wallet-connect-app-connected": "is connected", "wallet-connect-app-connected": "is connected",
"wallet-connect-connections-error": "Failed to get dApps connections",
"wallet-connect-go-back": "Go back to your browser or dapp", "wallet-connect-go-back": "Go back to your browser or dapp",
"wallet-connect-label": "WalletConnect", "wallet-connect-label": "WalletConnect",
"wallet-connect-networks-not-supported": "{{dapp}} requires an unsupported network.", "wallet-connect-networks-not-supported": "{{dapp}} requires an unsupported network.",