Compare commits

...
25 changed files with 532 additions and 315 deletions
+3 -2
View File
@@ -4,7 +4,8 @@
["@walletconnect/utils" :as wc-utils]
["@walletconnect/web3wallet$default" :as Web3Wallet]
[cljs-bean.core :as bean]
[oops.core :as oops]))
[oops.core :as oops]
[utils.transforms :as transforms]))
(defn- wallet-connect-core
[project-id]
@@ -34,7 +35,7 @@
(defn parse-uri
[uri]
(-> (oops/ocall wc-utils "parseUri" uri)
(bean/->clj)))
transforms/js->clj))
(defn respond-session-request
[{:keys [web3-wallet topic id result error]}]
@@ -7,7 +7,6 @@
[status-im.common.validation.general :as validators]
[status-im.contexts.communities.events]
[status-im.contexts.wallet.wallet-connect.utils.uri :as wc-uri]
[status-im.feature-flags :as ff]
[utils.address :as utils-address]
[utils.debounce :as debounce]
[utils.ethereum.eip.eip681 :as eip681]
@@ -99,9 +98,7 @@
;; TODO: https://github.com/status-im/status-mobile/issues/18744
nil
(and
(wc-uri/valid-uri? scanned-text)
(ff/enabled? ::ff/wallet.wallet-connect))
(-> scanned-text wc-uri/parse wc-uri/valid?)
(handle-wallet-connect scanned-text)
(url? scanned-text)
@@ -0,0 +1,19 @@
(ns status-im.contexts.wallet.common.utils.account
(:require [clojure.string :as string]))
(defn filter-operable
[accounts]
(filter #(and (:operable? %)
(not (:watch-only? %)))
accounts))
(defn to-eip155
[address chain-id]
(str chain-id ":" address))
(defn from-eip155
[address]
(-> address
(string/split #":")
last))
@@ -9,7 +9,7 @@
[status-im.common.resources :as resources]
[status-im.contexts.wallet.connected-dapps.disconnect-dapp.view :as disconnect-dapp]
[status-im.contexts.wallet.connected-dapps.style :as style]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as data-store]
[status-im.contexts.wallet.wallet-connect.utils.dapp :as dapp]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.string]))
@@ -107,17 +107,15 @@
:content-container-style (style/dapps-list theme)
:render-fn (fn [{:keys [topic pairingTopic name url iconUrl]}]
[quo/dapp
{:dapp {:avatar
(data-store/compute-dapp-icon-path
iconUrl
url)
:name
(data-store/compute-dapp-name
name
url)
:value url
:topic topic
:pairing-topic pairingTopic
{:dapp {:avatar (dapp/compute-icon
iconUrl
url)
:name (dapp/compute-name
name
url)
:value url
:topic topic
:pairing-topic pairingTopic
:customization-color customization-color}
:accessibility-label (str "dapp-" topic)
:state :default
@@ -4,7 +4,7 @@
[react-native.wallet-connect :as wallet-connect]
[status-im.config :as config]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.session :as session]
[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.typed-data :as typed-data]
@@ -44,14 +44,14 @@
(rf/reg-fx
:effects.wallet-connect/disconnect
(fn [{:keys [web3-wallet topic on-success on-fail]}]
(-> (sessions/disconnect web3-wallet topic)
(-> (session/disconnect web3-wallet topic)
(promesa/then on-success)
(promesa/catch on-fail))))
(rf/reg-fx
:effects.wallet-connect/approve-session
(fn [{:keys [web3-wallet proposal-request session-networks address on-success on-fail]}]
(-> (sessions/approve
(-> (session/approve
{:web3-wallet web3-wallet
:proposal-request proposal-request
:address address
@@ -145,6 +145,6 @@
(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?)
(-> (session/get-sessions web3-wallet addresses online?)
(promesa/then on-success)
(promesa/catch on-error))))
@@ -1,11 +1,12 @@
(ns status-im.contexts.wallet.wallet-connect.events.session-proposals
(:require [clojure.string :as string]
[re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.common.utils.account :as account-utils]
[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.proposal :as session-proposal]
[status-im.contexts.wallet.wallet-connect.utils.session :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.uri :as uri]
[taoensso.timbre :as log]
[utils.i18n :as i18n]))
@@ -23,37 +24,27 @@
(rf/reg-event-fx
:wallet-connect/on-scan-connection
(fn [{:keys [db]} [scanned-text]]
(let [network-status (:network/status db)
parsed-uri (wallet-connect/parse-uri scanned-text)
version (:version parsed-uri)
valid-wc-uri? (uri/valid-wc-uri? parsed-uri)
expired? (-> parsed-uri
:expiryTimestamp
uri/timestamp-expired?)
version-supported? (uri/version-supported? version)]
(if (or (not valid-wc-uri?)
(not version-supported?)
(= network-status :offline)
expired?)
(let [network-status (:network/status db)
parsed-uri (uri/parse scanned-text)
error-toast (cond
(= network-status :offline)
(i18n/label :t/wallet-connect-no-internet-warning)
(not (uri/valid? parsed-uri))
(i18n/label :t/wallet-connect-wrong-qr)
(uri/expired? parsed-uri)
(i18n/label :t/wallet-connect-qr-expired)
(not (uri/version-supported? parsed-uri))
(i18n/label :t/wallet-connect-version-not-supported
{:version (:version parsed-uri)}))]
(if error-toast
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme :dark
:text (cond (= network-status :offline)
(i18n/label :t/wallet-connect-no-internet-warning)
(not valid-wc-uri?)
(i18n/label :t/wallet-connect-wrong-qr)
expired?
(i18n/label :t/wallet-connect-qr-expired)
(not version-supported?)
(i18n/label :t/wallet-connect-version-not-supported
{:version version})
:else
(i18n/label :t/something-went-wrong))}]]]}
:text error-toast}]]]}
{:fx [[:dispatch [:wallet-connect/pair scanned-text]]]}))))
(rf/reg-event-fx
@@ -63,11 +54,12 @@
(let [accounts (get-in db [:wallet :accounts])
current-viewing-address (get-in db [:wallet :current-viewing-account-address])
sessions (get db :wallet-connect/sessions)
available-accounts (sessions/filter-operable-accounts (vals accounts))
available-accounts (-> accounts vals account-utils/filter-operable)
latest-connected-account-address (sessions/latest-connected-account-address sessions)
networks (networks/get-networks-by-mode db)
session-networks (networks/proposal-networks-intersection proposal networks)
required-networks-supported? (networks/required-networks-supported? proposal networks)]
session-networks (session-proposal/networks-intersection proposal networks)
required-networks-supported? (session-proposal/required-networks-supported? proposal
networks)]
(if (and (not-empty session-networks) required-networks-supported?)
{:db (update db
:wallet-connect/current-proposal assoc
@@ -91,13 +83,12 @@
(rf/reg-event-fx
:wallet-connect/show-session-networks-unsupported-toast
(fn [{:keys [db]} [proposal]]
(let [{:keys [name url]} (data-store/get-session-dapp-metadata proposal)]
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme (:theme db)
:text (i18n/label :t/wallet-connect-networks-not-supported
{:dapp (data-store/compute-dapp-name name url)})}]]]})))
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme (:theme db)
:text (i18n/label :t/wallet-connect-networks-not-supported
{:dapp (session-proposal/dapp-name proposal)})}]]]}))
(rf/reg-event-fx
:wallet-connect/reset-current-session-proposal
@@ -118,13 +109,10 @@
(map networks/chain-id->eip155)
vec)
current-address (get-in db [:wallet-connect/current-proposal :address])
network-status (:network/status db)
expired? (-> current-proposal
(get-in [:params :expiryTimestamp])
uri/timestamp-expired?)]
network-status (:network/status db)]
(if (= network-status :online)
{:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true)
:fx [(if expired?
:fx [(if (session-proposal/expired? current-proposal)
[:dispatch
[:toasts/upsert
{:id :wallet-connect-proposal-expired
@@ -173,11 +161,9 @@
(fn [{:keys [db]} [proposal]]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)
networks (networks/get-proposal-networks (or proposal request))
rejected? (nil? proposal)
dapp-name (-> (data-store/get-session-dapp-metadata (or proposal
request))
:name)]
networks (session-proposal/networks (or proposal request))
dapp-name (session-proposal/dapp-name (or proposal request))
rejected? (nil? proposal)]
{:fx (concat
(when-not response-sent?
[[:effects.wallet-connect/reject-session-proposal
@@ -6,29 +6,32 @@
[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.request :as session-request]
[status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
[utils.transforms :as transforms]
[cljs.pprint :as pprint]))
(rf/reg-event-fx
:wallet-connect/show-request-modal
(fn [{:keys [db]}]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)
screen (data-store/method-to-screen method)]
(let [screen (-> db
data-store/get-db-current-request-event
session-request/screen)]
(if screen
{:fx [[:dispatch [:open-modal screen]]]}
(log/error "Didn't find screen for Wallet Connect method"
{:method method
:event :wallet-connect/process-session-request})))))
{:event :wallet-connect/process-session-request})))))
(rf/reg-event-fx
:wallet-connect/process-session-request
(fn [{:keys [db]} [event]]
(let [method (data-store/get-request-method event)
existing-event (get-in db [:wallet-connect/current-request :event])]
(pprint/pprint event)
(let [method (session-request/method event)
existing-event (data-store/get-db-current-request-event db)]
(log/info "Processing Wallet Connect session request" method)
;; NOTE: make sure we don't show two requests at the same time
(when-not existing-event
@@ -95,17 +98,14 @@
:address address
:raw-data prepared-tx
:transaction tx
:chain-id chain-id
:display-data display-data)})))
(rf/reg-event-fx
:wallet-connect/process-eth-send-transaction
(fn [{:keys [db]} [{:keys [on-success]}]]
(let [event (data-store/get-db-current-request-event db)
tx (-> event data-store/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
networks/eip155->chain-id)]
tx (-> event session-request/params first)
chain-id (session-request/chain event)]
(when tx
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
@@ -120,10 +120,8 @@
:wallet-connect/process-eth-sign-transaction
(fn [{:keys [db]}]
(let [event (data-store/get-db-current-request-event db)
tx (-> event data-store/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
networks/eip155->chain-id)]
tx (-> event session-request/params first)
chain-id (session-request/chain event)]
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
@@ -135,9 +133,9 @@
(fn [{:keys [db]}]
(try
(let [[address raw-data] (data-store/get-db-current-request-params db)
session-chain-id (-> (data-store/get-db-current-request-event db)
(get-in [:params :chainId])
networks/eip155->chain-id)
session-chain-id (-> db
data-store/get-db-current-request-event
session-request/chain)
typed-data (-> raw-data
transforms/js-parse
transforms/js->clj)
@@ -189,11 +187,10 @@
(rf/reg-event-fx
:wallet-connect/on-processing-error
(fn [{:keys [db]} [error]]
(let [{:keys [address event]} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)]
(let [event (data-store/get-db-current-request-event db)
method (session-request/method event)]
(log/error "Failed to process Wallet Connect request"
{:error error
:address address
:method method
:wallet-connect-event event
:event :wallet-connect/on-processing-error})
@@ -2,9 +2,8 @@
(: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.uri :as uri]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as data-store]
[status-im.contexts.wallet.wallet-connect.utils.request :as session-request]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.transforms :as transforms]))
@@ -12,18 +11,15 @@
(rf/reg-event-fx
:wallet-connect/respond-current-session
(fn [{:keys [db]} [password]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)
screen (data-store/method-to-screen method)
expiry (get-in event [:params :request :expiryTimestamp])]
(if (uri/timestamp-expired? expiry)
(let [event (data-store/get-db-current-request-event db)]
(if (session-request/expired? event)
{:fx [[:dispatch
[:toasts/upsert
{:id :new-wallet-account-created
:type :negative
:text (i18n/label :t/wallet-connect-request-expired)}]]
[:dispatch [:dismiss-modal screen]]]}
{:fx [(condp = method
[:dispatch [:dismiss-modal (session-request/screen event)]]]}
{:fx [(condp = (session-request/method event)
constants/wallet-connect-personal-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :personal-sign]]
@@ -58,7 +54,7 @@
:wallet-connect/respond-sign-typed-data
(fn [{:keys [db]} [password typed-data-version]]
(let [{:keys [address raw-data event]} (get db :wallet-connect/current-request)
chain-id (get-in event [:params :chainId])]
chain-id (session-request/chain event)]
{:fx [[:effects.wallet-connect/sign-typed-data
{:password password
:address address
@@ -71,8 +67,9 @@
(rf/reg-event-fx
:wallet-connect/respond-send-transaction-data
(fn [{:keys [db]} [password]]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data]
(let [{:keys [raw-data address event]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data
chain-id (session-request/chain event)]
{:fx [[:effects.wallet-connect/send-transaction
{:password password
:address address
@@ -100,7 +97,7 @@
:wallet-connect/on-sign-error
(fn [{:keys [db]} [error]]
(let [{:keys [raw-data address event]} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)]
method (session-request/method event)]
(log/error "Failed to sign Wallet Connect request"
{:error error
:address address
@@ -118,8 +115,8 @@
:wallet-connect/send-response
(fn [{:keys [db]} [{:keys [request result error]}]]
(when-let [{:keys [id topic] :as event} (or request
(get-in db [:wallet-connect/current-request :event]))]
(let [method (data-store/get-request-method event)
(data-store/get-db-current-request-event db))]
(let [method (session-request/method event)
web3-wallet (get db :wallet-connect/web3-wallet)]
{:db (assoc-in db [:wallet-connect/current-request :response-sent?] true)
:fx [[:effects.wallet-connect/respond-session-request
@@ -141,13 +138,14 @@
(rf/reg-event-fx
:wallet-connect/redirect-to-dapp
(fn [{:keys [db]} [url]]
(let [redirect-url (or url
(->> (get db :wallet-connect/current-request)
(data-store/get-current-request-dapp
(get db :wallet-connect/sessions))
:sessionJson
transforms/json->clj
data-store/get-dapp-redirect-url))]
(let [sessions (get db :wallet-connect/sessions)
redirect-url (or url
(-> db
data-store/get-db-current-request-event
(data-store/get-current-request-dapp sessions)
:sessionJson
transforms/json->clj
data-store/get-dapp-redirect-url))]
{:fx [[:effects/open-url redirect-url]]})))
(rf/reg-event-fx
@@ -155,15 +153,14 @@
(fn [{:keys [db]} _]
(let [screen (-> db
(get-in [:wallet-connect/current-request :event])
data-store/get-request-method
data-store/method-to-screen)]
session-request/screen)]
{:fx [[:dispatch [:dismiss-modal screen]]]})))
(rf/reg-event-fx
:wallet-connect/finish-session-request
(fn [{:keys [db]} [result]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)
method (session-request/method event)
toast-text (condp contains? method
constants/wallet-connect-message-signing-methods
(i18n/label :t/wallet-connect-message-request-success-toast)
@@ -192,11 +189,11 @@
(rf/reg-event-fx
:wallet-connect/on-request-modal-dismissed
(fn [{:keys [db]}]
(let [{:keys [response-sent? event] :as request} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)
dapp (->> (get db :wallet-connect/sessions)
(data-store/get-current-request-dapp request)
:name)]
(let [{:keys [response-sent? event]} (get db :wallet-connect/current-request)
method (session-request/method event)
dapp (->> (get db :wallet-connect/sessions)
(data-store/get-current-request-dapp event)
:name)]
{:fx (concat
(when-not response-sent?
[[:dispatch
@@ -3,9 +3,10 @@
[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.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.session :as session-utils]
[taoensso.timbre :as log]
[utils.i18n :as i18n]))
[utils.i18n :as i18n]
[status-im.contexts.wallet.common.utils.account :as account-utils]))
(rf/reg-event-fx
:wallet-connect/on-session-delete
@@ -58,7 +59,7 @@
(fn [{:keys [db]}]
(let [addresses (->> (get-in db [:wallet :accounts])
vals
sessions/filter-operable-accounts
account-utils/filter-operable
(map :address))]
(if (not (seq addresses))
;; NOTE: Re-trying to get active sessions if accounts weren't loaded yet during
@@ -94,7 +95,7 @@
:wallet-connect/sessions
(fn [sessions]
(->> new-session
sessions/sdk-session->db-session
session-utils/session->db
(conj sessions))))}))
(rf/reg-event-fx
@@ -3,14 +3,13 @@
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.wallet-connect.modals.common.header.style :as style]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]))
[status-im.contexts.wallet.wallet-connect.utils.dapp :as dapp-utils]))
(defn view
[{:keys [label dapp account]}]
[rn/view {:style style/header-container}
(let [{:keys [name iconUrl url]} dapp
image-source (data-store/compute-dapp-icon-path iconUrl url)]
image-source (dapp-utils/compute-icon iconUrl url)]
[rn/view {:style style/dapp-container}
[quo/summary-tag
{:type :dapp
@@ -7,22 +7,19 @@
[react-native.core :as rn]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.wallet.wallet-connect.modals.session-proposal.style :as style]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as data-store]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.string]))
(defn- dapp-metadata
[]
(let [proposer (rf/sub [:wallet-connect/session-proposer])
{:keys [icons name url]} (:metadata proposer)
first-icon (first icons)
dapp-name (data-store/compute-dapp-name name url)
profile-picture (data-store/compute-dapp-icon-path first-icon url)]
(let [dapp-name (rf/sub [:wallet-connect/session-proposer-name])
dapp-icon (rf/sub [:wallet-connect/session-proposer-icon])
dapp-url (rf/sub [:wallet-connect/session-proposer-url])]
[:<>
[rn/view {:style style/dapp-avatar}
[quo/user-avatar
{:profile-picture profile-picture
{:profile-picture dapp-icon
:size :big
:full-name dapp-name}]]
[quo/page-top
@@ -31,7 +28,7 @@
:context-tag {:type :icon
:size 32
:icon :i/link
:context url}}]]))
:context dapp-url}}]]))
(defn- approval-note
[]
@@ -0,0 +1,33 @@
(ns status-im.contexts.wallet.wallet-connect.utils.dapp
(:require
[clojure.string :as string]
utils.string))
(defn compute-name
"Sometimes dapps have no name or an empty name. Return url as name in that case"
[name url]
(if (seq name)
name
(when (seq url)
(-> url
utils.string/remove-trailing-slash
utils.string/remove-http-prefix
string/capitalize))))
(defn compute-icon
"Some dapps have icons with relative paths, make paths absolute in those cases, send nil if icon is missing"
[icon-path url]
(when (and (seq icon-path)
(seq url))
(if (string/starts-with? icon-path "http")
icon-path
(str (utils.string/remove-trailing-slash url) icon-path))))
(defn best-icon
"Find the icon format from the provided list that can be rendered (prefer images)"
[icons]
(let [image-extensions [".png" ".jpg" ".jpeg"]
image? (fn [icon]
(some #(string/ends-with? icon %) image-extensions))]
(or (first (filter image? icons))
(first icons))))
@@ -1,65 +1,22 @@
(ns status-im.contexts.wallet.wallet-connect.utils.data-store
(:require
[clojure.string :as string]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as wallet-utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.wallet-connect.utils.request :as session-request]
utils.string
[utils.transforms :as transforms]))
(defn compute-dapp-name
"Sometimes dapps have no name or an empty name. Return url as name in that case"
[name url]
(if (seq name)
name
(when (seq url)
(-> url
utils.string/remove-trailing-slash
utils.string/remove-http-prefix
string/capitalize))))
(defn compute-dapp-icon-path
"Some dapps have icons with relative paths, make paths absolute in those cases, send nil if icon is missing"
[icon-path url]
(when (and (seq icon-path)
(seq url))
(if (string/starts-with? icon-path "http")
icon-path
(str (utils.string/remove-trailing-slash url) icon-path))))
(def method-to-screen
{constants/wallet-connect-personal-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-v4-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-send-transaction-method :screen/wallet-connect.send-transaction
constants/wallet-connect-eth-sign-transaction-method :screen/wallet-connect.sign-transaction})
(defn extract-native-call-signature
[data]
(-> data transforms/json->clj :result))
(defn get-request-method
[event]
(get-in event [:params :request :method]))
(defn get-request-params
[event]
(get-in event [:params :request :params]))
(defn get-db-current-request-event
[db]
(get-in db [:wallet-connect/current-request :event]))
(defn get-session-dapp-metadata
[proposal]
(let [metadata (get-in proposal [:params :proposer :metadata])
origin (get-in proposal [:verifyContext :verified :origin])]
(or metadata {:url origin})))
(defn get-current-request-dapp
[request sessions]
(let [dapp-url (get-in request [:event :verifyContext :verified :origin])]
[event sessions]
(let [dapp-url (session-request/url event)]
(->> sessions
(filter (fn [session]
(= (utils.string/remove-trailing-slash dapp-url)
@@ -78,7 +35,7 @@
[db]
(-> db
get-db-current-request-event
get-request-params))
session-request/params))
(defn get-total-connected-dapps
[db]
@@ -8,14 +8,14 @@
(deftest get-current-request-dapp-test
(testing "returns the correct dapp based on the request's origin"
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
(let [request {:verifyContext {:verified {:origin "https://dapp.com"}}}
sessions [{:url "https://dapp.com"}
{:url "https://anotherdapp.com"}]]
(is (= {:url "https://dapp.com"}
(sut/get-current-request-dapp request sessions)))))
(testing "returns nil if no matching dapp is found"
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
(let [request {:verifyContext {:verified {:origin "https://dapp.com"}}}
sessions [{:url "https://anotherdapp.com"}]]
(is (nil? (sut/get-current-request-dapp request sessions))))))
@@ -17,10 +17,6 @@
last
edn/read-string))
(defn format-eip155-address
[address chain-id]
(str chain-id ":" address))
(defn- add-full-testnet-name
"Updates the `:full-name` key with the full testnet name if using testnet `:chain-id`.\n
e.g. `{:full-name \"Mainnet\"}` -> `{:full-name \"Mainnet Sepolia\"`}`"
@@ -49,33 +45,19 @@
(set/subset? chain-ids constants/sepolia-chain-ids)
(set/subset? chain-ids constants/mainnet-chain-ids))))
(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 networks-intersection
[proposed-networks supported-networks]
(->> supported-networks
(filter #(->> %
chain-id->eip155
(contains? proposed-networks)))))
(defn required-networks-supported?
[proposal supported-networks]
(let [supported-namespaces #{:eip155}
required-namespaces (get-in proposal [:params :requiredNamespaces])]
(when (every? #(contains? supported-namespaces %)
(keys required-namespaces))
(let [required-networks (get-in required-namespaces [:eip155 :chains])
supported-eip155 (set (map chain-id->eip155 supported-networks))]
(every? #(contains? supported-eip155 %)
required-networks)))))
[required-networks supported-networks]
(every? #(-> (map chain-id->eip155 supported-networks)
set
(contains? %))
required-networks))
(defn get-networks-by-mode
[db]
@@ -0,0 +1,135 @@
(ns status-im.contexts.wallet.wallet-connect.utils.proposal
(:require [clojure.string :as string]
[schema.core :as schema]
[status-im.contexts.wallet.wallet-connect.utils.dapp :as dapp-utils]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks-utils]
[utils.datetime :as datetime]))
(def ^:private ?proposer-metadata
[:map
[:name {:optional true} :string]
[:url {:optional true} :string]
[:description {:optional true} :string]
[:icons {:optional true} [:sequential :string]]])
(def ^:private ?namespace
[:map
[:methods [:sequential :string]]
[:chains [:sequential :string]]
[:events [:sequential :string]]])
(def ^:private ?proposal
[:map
[:id :int]
[:verifyContext
[:map
[:verified
[:map
[:validation :string]
[:origin :string]
[:verifyUrl :string]
[:isScam {:optional true} :boolean]]]]]
[:params
[:map
[:id :int]
[:expiryTimestamp :int]
[:pairingTopic :string]
[:requiredNamespaces [:map-of :keyword ?namespace]]
[:optionalNamespaces [:map-of :keyword ?namespace]]
[:relays [:sequential :any]]
[:proposer
[:map
[:publicKey :string]
[:metadata ?proposer-metadata]]]]]])
(defn expired?
[proposal]
(some-> proposal
:params
:expiryTimestamp
datetime/timestamp-expired?))
(schema/=> expired?
[:=>
[:cat ?proposal]
:boolean])
(defn dapp-metadata
[proposal]
(let [metadata (get-in proposal [:params :proposer :metadata])
origin (get-in proposal [:verifyContext :verified :origin])]
(or metadata {:url origin})))
(schema/=> dapp-metadata
[:=>
[:cat ?proposal]
?proposer-metadata])
(defn dapp-name
[proposal]
(let [{:keys [name url]} (-> proposal dapp-metadata)]
(dapp-utils/compute-name name url)))
(schema/=> dapp-name
[:=>
[:cat ?proposal]
[:maybe :string]])
(defn dapp-icon
[proposal]
(let [{:keys [icons url]} (-> proposal dapp-metadata)
icon (dapp-utils/best-icon icons)]
(dapp-utils/compute-icon icon url)))
(schema/=> dapp-icon
[:=>
[:cat ?proposal]
[:maybe :string]])
(defn dapp-url
[proposal]
(-> proposal dapp-metadata :url))
(schema/=> dapp-url
[:=>
[:cat ?proposal]
[:maybe :string]])
(defn 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 #{}))))
(schema/=> networks
[:=>
[:cat ?proposal]
[:set :string]])
(defn networks-intersection
[proposal supported-networks]
(let [proposed-networks (networks proposal)]
(networks-utils/networks-intersection proposed-networks
supported-networks)))
(schema/=> networks-intersection
[:=>
[:cat ?proposal [:sequential :int]]
[:sequential :int]])
(defn required-networks-supported?
[proposal supported-networks]
(let [supported-namespaces #{:eip155}
required-namespaces (get-in proposal [:params :requiredNamespaces])
required-networks (get-in required-namespaces [:eip155 :chains])]
(when (every? #(contains? supported-namespaces %)
(keys required-namespaces))
(networks-utils/required-networks-supported? required-networks supported-networks))))
(schema/=> required-networks-supported?
[:=>
[:cat ?proposal [:sequential :int]]
:boolean])
@@ -0,0 +1,87 @@
(ns status-im.contexts.wallet.wallet-connect.utils.request
(:require
[schema.core :as schema]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[utils.datetime :as datetime]))
(def ^:private ?event
[:map
[:id :string]
[:params
[:map
[:chainId :string]
[:request
[:map
[:method :string]
[:params :any]
[:expiryTimestamp :int]]]]]
[:verifyContext
[:map
[:verified
[:map
[:validation :string]
[:origin :string]
[:verifyUrl :string]
[:isScam {:optional true} :boolean]]]]]])
(defn expired?
[event]
(some-> event
:params
:request
:expiryTimestamp
datetime/timestamp-expired?))
(schema/=> expired?
[:=> [:cat ?event]
[:maybe :boolean]])
(defn method
[event]
(get-in event [:params :request :method]))
(schema/=> method
[:=> [:cat ?event]
:string])
(defn chain
[event]
(-> event
(get-in [:params :chainId])
networks/eip155->chain-id))
(schema/=> chain
[:=> [:cat ?event]
:int])
(defn screen
[event]
(let [method-to-screen
{constants/wallet-connect-personal-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-v4-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-send-transaction-method :screen/wallet-connect.send-transaction
constants/wallet-connect-eth-sign-transaction-method :screen/wallet-connect.sign-transaction}]
(-> event method method-to-screen)))
(schema/=> screen
[:=> [:cat ?event]
[:maybe :keyword]])
(defn params
[event]
(get-in event [:params :request :params]))
(schema/=> screen
[:=> [:cat ?event]
:any])
(defn url
[event]
(get-in event [:verifyContext :verified :origin]))
(schema/=> screen
[:=> [:cat ?event]
:string])
@@ -1,34 +1,53 @@
(ns status-im.contexts.wallet.wallet-connect.utils.sessions
(ns status-im.contexts.wallet.wallet-connect.utils.session
(:require
[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.common.utils.account :as account-utils]
[status-im.contexts.wallet.wallet-connect.utils.dapp :as dapp-utils]
[status-im.contexts.wallet.wallet-connect.utils.rpc :as rpc]
[taoensso.timbre :as log]
[utils.transforms :as transforms]))
(defn sdk-session->db-session
(defn- dapp-icon
[session]
(-> session
(get-in [:peer :metadata :icons])
dapp-utils/best-icon))
(defn- dapp-url
[session]
(get-in session [:peer :metadata :url]))
(defn- dapp-name
[session]
(-> session
(get-in [:peer :metadata :name])
(or (dapp-url session))))
(defn- addresses
[session]
(get-in session [:namespaces :eip155 :accounts]))
(defn- chains
[session]
(get-in session [:namespaces :eip155 :chains]))
(defn session->db
[{:keys [topic expiry pairingTopic] :as session}]
{:topic topic
:expiry expiry
:sessionJson (transforms/clj->json session)
:pairingTopic pairingTopic
:name (get-in session [:peer :metadata :name])
:iconUrl (get-in session [:peer :metadata :icons 0])
:url (get-in session [:peer :metadata :url])
:accounts (get-in session [:namespaces :eip155 :accounts])
:chains (get-in session [:namespaces :eip155 :chains])
:name (dapp-name session)
:iconUrl (dapp-icon session)
:url (dapp-url session)
:accounts (addresses session)
:chains (chains session)
:disconnected false})
(defn filter-operable-accounts
[accounts]
(filter #(and (:operable? %)
(not (:watch-only? %)))
accounts))
(defn filter-sessions-for-account-addresses
(defn filter-for-accounts
[account-addresses sessions]
(filter (fn [{:keys [accounts]}]
(some (fn [account]
@@ -40,24 +59,13 @@
(defn latest-connected-account-address
[sessions]
(let [all-accounts (->> sessions
(sort-by :expiry >)
first
:accounts)]
(-> all-accounts
(let [recent-session (->> sessions
(sort-by :expiry >)
first)]
(-> recent-session
:accounts
first
(string/split #":")
last)))
(defn- parse-session-accounts
[{:keys [sessionJson] :as session}]
(assoc session
:accounts
(-> sessionJson
transforms/json->clj
:namespaces
:eip155
:accounts)))
account-utils/from-eip155)))
(defn- find-inactive-sessions
[active-sessions persisted-sessions]
@@ -72,20 +80,20 @@
(defn get-persisted-sessions
[]
(-> (rpc/wallet-get-persisted-sessions)
(promesa/then #(map parse-session-accounts %))
(promesa/then #(map :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]
[web3-wallet account-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)))
(map session->db)
(filter-for-accounts account-addresses)))
(promesa/catch (fn [err]
(throw (ex-info "Failed to get active WalletConnect sessions"
{:error err
@@ -104,10 +112,10 @@
:code :error/wc-sync-persisted-sessions}))))))
(defn get-sessions
[web3-wallet addresses online?]
[web3-wallet accounts online?]
(promesa/let [persisted-sessions (get-persisted-sessions)]
(if online?
(promesa/let [active-sessions (get-active-sessions web3-wallet addresses)]
(promesa/let [active-sessions (get-active-sessions web3-wallet accounts)]
(log/info "Got active Wallet Connect sessions" (map :topic active-sessions))
;; NOTE: handling the error here, so that if persistance fails, it doesn't affect the active
;; sessions
@@ -133,7 +141,7 @@
(defn approve
[{:keys [web3-wallet address session-networks proposal-request]}]
(let [{:keys [params id]} proposal-request
accounts (-> (partial networks/format-eip155-address address)
session-accounts (-> (partial account-utils/to-eip155 address)
(map session-networks))]
(-> (promesa/let [session
(wallet-connect/approve-session
@@ -142,7 +150,7 @@
:approved-namespaces (->>
{:eip155
{:chains session-networks
:accounts accounts
:accounts session-accounts
:methods constants/wallet-connect-supported-methods
:events constants/wallet-connect-supported-events}}
(wallet-connect/build-approved-namespaces params))})]
@@ -5,15 +5,14 @@
[promesa.core :as promesa]
[status-im.constants :as constants]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.request :as session-request]
[status-im.contexts.wallet.wallet-connect.utils.rpc :as wallet-connect-rpc]
[utils.money :as money]
[utils.transforms :as transforms]))
(defn transaction-request?
[event]
(->> (data-store/get-request-method event)
(->> (session-request/method event)
(contains? #{constants/wallet-connect-eth-send-transaction-method
constants/wallet-connect-eth-sign-transaction-method})))
@@ -85,9 +85,8 @@
data-chain-id)))
(defn sign
[password address data chain-id-eip155 version]
(let [legacy? (= version :v1)
chain-id (networks/eip155->chain-id chain-id-eip155)]
[password address data chain-id version]
(let [legacy? (= version :v1)]
(wallet-rpc/safe-sign-typed-data data
address
password
@@ -1,29 +1,56 @@
(ns status-im.contexts.wallet.wallet-connect.utils.uri
(:require [react-native.wallet-connect :as wallet-connect]))
"Validating and parsing WalletConnect pairing URIs"
(:require [malli.util :as malli.util]
[react-native.wallet-connect :as wallet-connect]
[schema.core :as schema]
[utils.datetime :as datetime]))
(def ?wc-uri
[:map
[:protocol :string]
[:topic :string]
[:version :int]
[:symKey [:maybe :string]]
[:relay :map]
[:methods {:optional true} [:maybe [:sequential :any]]]
[:expiryTimestamp [:maybe :int]]])
(defn parse
[text-uri]
(wallet-connect/parse-uri text-uri))
(schema/=> parse
[:=>
[:cat :string]
?wc-uri])
(defn version-supported?
[version]
(= version 2))
[uri]
(= 2 (:version uri)))
(defn- current-timestamp
[]
(quot (.getTime (js/Date.)) 1000))
(schema/=> version-supported?
[:=>
[:cat ?wc-uri]
:boolean])
(defn timestamp-expired?
[expiry-timestamp]
(when expiry-timestamp
(> (current-timestamp) expiry-timestamp)))
(defn expired?
[uri]
(some-> uri
:expiryTimestamp
datetime/timestamp-expired?))
(defn valid-wc-uri?
[parsed-uri]
(let [{:keys [topic version]} parsed-uri]
(and (seq topic)
(number? version))))
(schema/=> expired?
[:=>
[:cat ?wc-uri]
[:maybe :boolean]])
(defn valid-uri?
"Check if the uri is in the wallet-connect format.
At this stage, the uri might be expired or from an unsupported version"
[s]
(-> s
wallet-connect/parse-uri
valid-wc-uri?))
(defn valid?
[uri]
(let [{:keys [topic version]} uri]
(boolean (and (seq topic)
(number? version)))))
(schema/=> valid?
[:=>
[:cat (malli.util/optional-keys ?wc-uri)]
[:maybe :boolean]])
+13 -9
View File
@@ -1,7 +1,6 @@
(ns status-im.subs.wallet.dapps.proposals
(:require [re-frame.core :as rf]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.proposal :as proposal]
[utils.string]))
(rf/reg-sub
@@ -15,17 +14,22 @@
:-> :session-networks)
(rf/reg-sub
:wallet-connect/session-proposer
:wallet-connect/session-proposer-name
:<- [:wallet-connect/current-proposal-request]
(fn [proposal]
(-> proposal :params :proposer)))
(proposal/dapp-name proposal)))
(rf/reg-sub
:wallet-connect/session-proposer-name
:<- [:wallet-connect/session-proposer]
(fn [proposer]
(let [{:keys [name url]} (-> proposer :metadata)]
(data-store/compute-dapp-name name url))))
:wallet-connect/session-proposer-icon
:<- [:wallet-connect/current-proposal-request]
(fn [proposal]
(proposal/dapp-icon proposal)))
(rf/reg-sub
:wallet-connect/session-proposer-url
:<- [:wallet-connect/current-proposal-request]
(fn [proposal]
(proposal/dapp-url proposal)))
(rf/reg-sub
:wallet-connect/session-proposal-network-details
@@ -77,22 +77,6 @@
:origin "https://lab.web3modal.com"
:isScam false}}}})
(h/deftest-sub :wallet-connect/session-proposer
[sub-name]
(testing "Return the session proposer public key and metadata"
(swap! rf-db/app-db
assoc-in
[:wallet-connect/current-proposal :request]
sample-session)
(let [proposer (rf/sub [sub-name])]
(is (= (-> proposer :publicKey)
(-> sample-session :params :proposer :publicKey)))
(is (= (-> proposer :metadata :url)
(-> sample-session :params :proposer :metadata :url))))))
(h/deftest-sub :wallet-connect/session-proposer-name
[sub-name]
(testing "Return only the name of the session proposer"
@@ -4,6 +4,7 @@
[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.request :as session-request]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
[utils.string]))
@@ -23,7 +24,7 @@
(fn [request]
(-> request
:event
data-store/get-request-method)))
session-request/method)))
(rf/reg-sub
:wallet-connect/current-request-account-details
@@ -40,15 +41,17 @@
:<- [:wallet-connect/current-request]
:<- [:wallet-connect/sessions]
(fn [[request sessions]]
(data-store/get-current-request-dapp request sessions)))
(-> request
:event
(data-store/get-current-request-dapp sessions))))
(rf/reg-sub
:wallet-connect/chain-id
:<- [:wallet-connect/current-request]
(fn [request]
(-> request
(get-in [:event :params :chainId])
(networks/eip155->chain-id))))
:event
session-request/chain)))
(rf/reg-sub
:wallet-connect/current-request-network
+7
View File
@@ -325,3 +325,10 @@
(def ^:const go-default-time
"Zero value for golang's time var"
"0001-01-01T00:00:00Z")
(defn timestamp-expired?
[ms]
(-> ms
(* 1000)
to-date
(t/before? (now))))