Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keycard signing for dapp interactions #21785

Merged
merged 13 commits into from
Jan 28, 2025
16 changes: 10 additions & 6 deletions src/native_module/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -253,15 +253,19 @@

(defn hash-typed-data
"used for keycard"
[data callback]
(log/debug "[native-module] hash-typed-data")
(.hashTypedData ^js (encryption) data callback))
([data]
(native-utils/promisify-native-module-call hash-typed-data data))
([data callback]
(log/debug "[native-module] hash-typed-data")
(.hashTypedData ^js (encryption) data callback)))

(defn hash-typed-data-v4
"used for keycard"
[data callback]
(log/debug "[native-module] hash-typed-data-v4")
(.hashTypedDataV4 ^js (encryption) data callback))
([data]
(native-utils/promisify-native-module-call hash-typed-data-v4 data))
([data callback]
(log/debug "[native-module] hash-typed-data-v4")
(.hashTypedDataV4 ^js (encryption) data callback)))

(defn send-transaction-with-signature
"used for keycard"
Expand Down
7 changes: 1 addition & 6 deletions src/status_im/constants.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,7 @@
(def ^:const wallet-connect-eth-sign-typed-v4-method "eth_signTypedData_v4")
(def ^:const wallet-connect-supported-methods
#{wallet-connect-personal-sign-method
wallet-connect-eth-sign-method
wallet-connect-eth-send-transaction-method
;; NOTE: disabled, as we have no clear use cases for it and other wallets don't support it
;; wallet-connect-eth-sign-transaction-method
wallet-connect-eth-sign-typed-method
wallet-connect-eth-sign-typed-v4-method})
(def ^:const wallet-connect-supported-events #{"accountsChanged" "chainChanged"})
Expand All @@ -237,11 +234,9 @@
(def ^:const wallet-connect-message-signing-methods
#{wallet-connect-personal-sign-method
wallet-connect-eth-sign-typed-method
wallet-connect-eth-sign-method
wallet-connect-eth-sign-typed-v4-method})
(def ^:const wallet-connect-transaction-methods
#{wallet-connect-eth-send-transaction-method
wallet-connect-eth-sign-transaction-method})
#{wallet-connect-eth-send-transaction-method})

(def ^:const transaction-pending-type-wallet-connect-transfer "WalletConnectTransfer")

Expand Down
13 changes: 13 additions & 0 deletions src/status_im/contexts/keycard/utils.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,16 @@
args
:on-success (get-on-success args)
:on-failure (get-on-failure args)))

(defn keycard-address?
[keypairs address]
(let [find-keycard-keypair (fn [kps] (some #(when-not (empty? (:keycards %)) %) kps))
keypair-addresses (fn [kp]
(->> (:accounts kp)
(map :address)
set))]
(-> keypairs
vals
find-keycard-keypair
keypair-addresses
(contains? (string/lower-case address)))))
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
type :no-title}}]
(let [{:keys [color emoji watch-only?]} (rf/sub [:wallet/current-viewing-account])
networks (rf/sub [:wallet/selected-network-details])
sending-collectible? (rf/sub [:wallet/sending-collectible?])
keycard? (rf/sub [:wallet/selected-keypair-keycard?])]
sending-collectible? (rf/sub [:wallet/sending-collectible?])]
[quo/page-nav
{:type type
:icon-name icon-name
Expand All @@ -46,11 +45,7 @@
(not watch-only?)
show-dapps-button?)
{:icon-name :i/dapps
:on-press #(rf/dispatch
(if keycard?
[:keycard/feature-unavailable-show
{:feature-name :wallet.show-connected-dapps}]
[:navigate-to :screen/wallet.connected-dapps]))})
:on-press #(rf/dispatch [:navigate-to :screen/wallet.connected-dapps])})
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
Expand Down
7 changes: 7 additions & 0 deletions src/status_im/contexts/wallet/effects.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,10 @@
(-> (sign-transaction-hashes hashes address password)
(promesa/then on-success)
(promesa/catch on-error))))

(rf/reg-fx
:effects.wallet/sign-message
(fn [{:keys [message address password on-success on-error]}]
(-> (wallet-rpc/sign-message message address (security/safe-unmask-data password))
(promesa/then on-success)
(promesa/catch on-error))))
2 changes: 1 addition & 1 deletion src/status_im/contexts/wallet/rpc.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
chain-id
constants/transaction-pending-type-wallet-connect-transfer
(transforms/js-stringify tx-args 0)
signature))
(utils.hex/normalize-hex signature)))

(defn sign-message
[message address password]
Expand Down
65 changes: 21 additions & 44 deletions src/status_im/contexts/wallet/wallet_connect/events/effects.cljs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
(ns status-im.contexts.wallet.wallet-connect.events.effects
(:require
[native-module.core :as native-module]
[promesa.core :as promesa]
[react-native.wallet-connect :as wallet-connect]
[status-im.config :as config]
[status-im.constants :as constants]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[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.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
[utils.transforms :as transforms]))

(rf/reg-fx
:effects.wallet-connect/init
Expand Down Expand Up @@ -69,20 +69,21 @@
(promesa/catch (partial rf/call-continuation on-fail)))))

(rf/reg-fx
:effects.wallet-connect/sign-message
(fn [{:keys [password address data rpc-method on-success on-error]}]
(let [password (security/safe-unmask-data password)]
(-> (condp =
rpc-method
:personal-sign
(signing/personal-sign password address data)

:eth-sign
(signing/eth-sign password address data)
:effects.wallet-connect/hash-message
(fn [{:keys [message on-success on-fail]}]
(-> (wallet-rpc/hash-message-eip-191 message)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-fail)))))

(signing/personal-sign password address data))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error))))))
(rf/reg-fx
:effects.wallet-connect/hash-typed-data
(fn [{:keys [message legacy? on-success on-fail]}]
(-> (if legacy?
(native-module/hash-typed-data message)
(native-module/hash-typed-data-v4 message))
(promesa/then (fn [response] (-> response transforms/json->clj :result)))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-fail)))))

(rf/reg-fx
:effects.wallet-connect/prepare-transaction
Expand All @@ -93,36 +94,12 @@
(promesa/then on-success)
(promesa/catch on-error))))

(rf/reg-fx
:effects.wallet-connect/sign-transaction
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
(-> (transactions/sign-transaction (security/safe-unmask-data password)
address
tx-hash
tx-args
chain-id)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))

(rf/reg-fx
:effects.wallet-connect/send-transaction
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
(-> (transactions/send-transaction (security/safe-unmask-data password)
address
tx-hash
tx-args
chain-id)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))

(rf/reg-fx
:effects.wallet-connect/sign-typed-data
(fn [{:keys [password address data version chain-id on-success on-error]}]
(-> (typed-data/sign (security/safe-unmask-data password)
address
data
chain-id
version)
(fn [{:keys [chain-id signature tx-args on-success on-error]}]
(-> (wallet-rpc/send-transaction-with-signature chain-id
tx-args
signature)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
(:require [clojure.string :as string]
[re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.contexts.wallet.data-store :as wallet-data-store]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
Expand Down Expand Up @@ -46,15 +45,8 @@
expired? (-> parsed-uri
:expiryTimestamp
uri/timestamp-expired?)
version-supported? (uri/version-supported? version)
keycard? (wallet-data-store/selected-keypair-keycard? db)]
version-supported? (uri/version-supported? version)]
(cond

keycard?
{:fx [[:dispatch
[:keycard/feature-unavailable-show
{:feature-name :wallet.scan-dapp-connection}]]]}

(or (not valid-wc-uri?)
(not version-supported?)
(= network-status :offline)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@
[:wallet-connect/process-eth-send-transaction
{:on-success (fn [] (rf/dispatch [:wallet-connect/show-request-modal]))}]]

constants/wallet-connect-eth-sign-method
[:dispatch [:wallet-connect/process-eth-sign]]

constants/wallet-connect-eth-sign-transaction-method
[:dispatch [:wallet-connect/process-eth-sign-transaction]]

constants/wallet-connect-eth-sign-typed-method
[:dispatch [:wallet-connect/process-sign-typed]]

Expand All @@ -57,46 +51,44 @@
[:dispatch [:wallet-connect/process-personal-sign]])]}))))

(rf/reg-event-fx
:wallet-connect/process-personal-sign
(fn [{:keys [db]}]
(let [[raw-data address] (data-store/get-db-current-request-params db)
parsed-data (native-module/hex-to-utf8 raw-data)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:raw-data raw-data
:display-data (or parsed-data raw-data))
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
:wallet-connect/store-prepared-hash
(fn [{:keys [db]} [prepared-hash]]
{:db (assoc-in db
[:wallet-connect/current-request :prepared-hash]
prepared-hash)}))

(rf/reg-event-fx
:wallet-connect/process-eth-sign
:wallet-connect/process-personal-sign
(fn [{:keys [db]}]
(let [[address raw-data] (data-store/get-db-current-request-params db)
(let [[raw-data address] (data-store/get-db-current-request-params db)
parsed-data (native-module/hex-to-utf8 raw-data)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:raw-data raw-data
:display-data (or parsed-data raw-data))
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
:fx [[:effects.wallet-connect/hash-message
{:message raw-data
:on-success #(rf/dispatch [:wallet-connect/store-prepared-hash %])
:on-fail #(rf/dispatch [:wallet-connect/on-processing-error %])}]
[:dispatch [:wallet-connect/show-request-modal]]]})))

(rf/reg-event-fx
:wallet-connect/prepare-transaction-success
(fn [{:keys [db]} [prepared-tx chain-id]]
(let [{:keys [tx-args]} prepared-tx
tx (bean/->clj tx-args)
address (-> tx :from string/lower-case)
display-data (transactions/beautify-transaction tx)]
(let [{:keys [tx-args tx-hash]} prepared-tx
tx (bean/->clj tx-args)
address (-> tx :from string/lower-case)
display-data (transactions/beautify-transaction tx)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address address
:raw-data prepared-tx
:transaction tx
:chain-id chain-id
:display-data display-data)})))
:display-data display-data)
:fx [[:dispatch [:wallet-connect/store-prepared-hash tx-hash]]]})))

(rf/reg-event-fx
:wallet-connect/process-eth-send-transaction
Expand All @@ -116,25 +108,14 @@
(rf/call-continuation on-success)))
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]}))))

(rf/reg-event-fx
: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)]
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
:on-success #(rf/dispatch [:wallet-connect/prepare-transaction-success % chain-id])
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]})))

(rf/reg-event-fx
:wallet-connect/process-sign-typed
(fn [{:keys [db]}]
(try
(let [[address raw-data] (data-store/get-db-current-request-params db)
method (-> db
data-store/get-db-current-request-event
data-store/get-request-method)
session-chain-id (-> (data-store/get-db-current-request-event db)
(get-in [:params :chainId])
networks/eip155->chain-id)
Expand All @@ -154,7 +135,13 @@
:address (string/lower-case address)
:display-data (typed-data/flatten-typed-data typed-data)
:raw-data raw-data)
:fx [[:dispatch [:wallet-connect/show-request-modal]]]}))
:fx [[:effects.wallet-connect/hash-typed-data
{:message raw-data
:legacy? (not= constants/wallet-connect-eth-sign-typed-v4-method
method)
:on-success #(rf/dispatch [:wallet-connect/store-prepared-hash %])
:on-fail #(rf/dispatch [:wallet-connect/on-processing-error %])}]
[:dispatch [:wallet-connect/show-request-modal]]]}))
(catch js/Error err
{:fx [[:dispatch
[:wallet-connect/on-processing-error
Expand Down
Loading