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:
parent
97aff27980
commit
d748ccdef9
|
@ -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])]})))
|
||||||
|
|
||||||
|
|
|
@ -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]}]]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))))
|
||||||
|
|
|
@ -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]]]})))
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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" %)}]]]}))
|
|
||||||
|
|
|
@ -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"]))))))
|
|
@ -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)
|
||||||
|
|
|
@ -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})))))))
|
||||||
|
|
|
@ -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.",
|
||||||
|
|
Loading…
Reference in New Issue