From 588692e0ebeb5d967c1da22292653bc09c740133 Mon Sep 17 00:00:00 2001 From: Lungu Cristian Date: Tue, 16 Jul 2024 14:36:16 +0300 Subject: [PATCH] Wallet Connect message signing (#20693) * feat: updated signing endpoints and refactor https://github.com/status-im/status-go/compare/6e056348...e8aec741 * fix: address review comments --- ios/Podfile.lock | 4 +- src/legacy/status_im/utils/hex.cljs | 16 ----- src/status_im/contexts/wallet/send/utils.cljs | 2 +- .../wallet/wallet_connect/effects.cljs | 49 +++++++++------ .../wallet_connect/responding_events.cljs | 28 +++------ .../contexts/wallet/wallet_connect/rpc.cljs | 61 +++++++++++++++++++ .../wallet/wallet_connect/signing.cljs | 32 ++++++++++ .../wallet/wallet_connect/transactions.cljs | 58 +++--------------- src/status_im/subs/wallet/activities.cljs | 2 +- src/status_im/subs/wallet/wallet_connect.cljs | 2 +- src/utils/hex.cljs | 27 ++++++++ 11 files changed, 171 insertions(+), 110 deletions(-) delete mode 100644 src/legacy/status_im/utils/hex.cljs create mode 100644 src/status_im/contexts/wallet/wallet_connect/rpc.cljs create mode 100644 src/status_im/contexts/wallet/wallet_connect/signing.cljs create mode 100644 src/utils/hex.cljs diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b48f3e9ca2..25e7f83d35 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -915,7 +915,7 @@ PODS: - glog - RCT-Folly (= 2022.05.16.00) - React-Core - - react-native-compat (2.12.2): + - react-native-compat (2.11.2): - glog - RCT-Folly (= 2022.05.16.00) - React-Core @@ -1517,7 +1517,7 @@ SPEC CHECKSUMS: react-native-blob-util: 600972b1782380a5a7d5db61a3817ea32349dae9 react-native-blur: 799045500f56146afc46245148080e7b7623cb75 react-native-cameraroll: af8eec1e585d053ff485d98ec837f9a8a11b5745 - react-native-compat: 84e00e8dcff9251278c0d48f2bce81f4502e3925 + react-native-compat: 3af9add14d349701306d3d052638435f6795ac2c react-native-config: 5330c8258265c1e5fdb8c009d2cabd6badd96727 react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06 react-native-hole-view: 6935448993bac79f2b5a4ad7e9741094cf810679 diff --git a/src/legacy/status_im/utils/hex.cljs b/src/legacy/status_im/utils/hex.cljs deleted file mode 100644 index c292732b7b..0000000000 --- a/src/legacy/status_im/utils/hex.cljs +++ /dev/null @@ -1,16 +0,0 @@ -(ns legacy.status-im.utils.hex - (:require - [clojure.string :as string])) - -(defn normalize-hex - [hex] - (when hex - (string/lower-case (if (string/starts-with? hex "0x") - (subs hex 2) - hex)))) - -(defn valid-hex? - [hex] - (let [hex (normalize-hex hex)] - (and (re-matches #"^[0-9a-fA-F]+$" hex) - (not= (js/parseInt hex 16) 0)))) diff --git a/src/status_im/contexts/wallet/send/utils.cljs b/src/status_im/contexts/wallet/send/utils.cljs index d297a3981d..479547a6ec 100644 --- a/src/status_im/contexts/wallet/send/utils.cljs +++ b/src/status_im/contexts/wallet/send/utils.cljs @@ -1,8 +1,8 @@ (ns status-im.contexts.wallet.send.utils (:require - [legacy.status-im.utils.hex :as utils.hex] [native-module.core :as native-module] [status-im.contexts.wallet.common.utils.networks :as network-utils] + [utils.hex :as utils.hex] [utils.money :as money])) (defn amount-in-hex diff --git a/src/status_im/contexts/wallet/wallet_connect/effects.cljs b/src/status_im/contexts/wallet/wallet_connect/effects.cljs index cb9364e7be..40c12f6ef4 100644 --- a/src/status_im/contexts/wallet/wallet_connect/effects.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/effects.cljs @@ -1,17 +1,14 @@ (ns status-im.contexts.wallet.wallet-connect.effects (:require - [cljs-bean.core :as bean] - [native-module.core :as native-module] [promesa.core :as promesa] [re-frame.core :as rf] [react-native.wallet-connect :as wallet-connect] [status-im.config :as config] [status-im.constants :as constants] - [status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core] + [status-im.contexts.wallet.wallet-connect.signing :as signing] [status-im.contexts.wallet.wallet-connect.transactions :as transactions] [utils.i18n :as i18n] - [utils.security.core :as security] - [utils.transforms :as transforms])) + [utils.security.core :as security])) (rf/reg-fx :effects.wallet-connect/init @@ -79,36 +76,48 @@ (rf/reg-fx :effects.wallet-connect/sign-message - (fn [{:keys [password address data on-success on-error]}] - (-> {:data data - :account address - :password (security/safe-unmask-data password)} - bean/->js - transforms/clj->json - native-module/sign-message - (promesa/then wallet-connect-core/extract-native-call-signature) - (promesa/then on-success) - (promesa/catch on-error)))) + (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) + + (signing/personal-sign password address data)) + (promesa/then on-success) + (promesa/catch on-error))))) (rf/reg-fx :effects.wallet-connect/sign-transaction (fn [{:keys [password address chain-id tx on-success on-error]}] - (-> (transactions/sign-transaction (security/safe-unmask-data password) address tx chain-id) + (-> (transactions/sign-transaction (security/safe-unmask-data password) + address + tx + chain-id) (promesa/then on-success) (promesa/catch on-error)))) (rf/reg-fx :effects.wallet-connect/send-transaction (fn [{:keys [password address chain-id tx on-success on-error]}] - (-> (transactions/send-transaction (security/safe-unmask-data password) address tx chain-id) + (-> (transactions/send-transaction (security/safe-unmask-data password) + address + tx + chain-id) (promesa/then on-success) (promesa/catch on-error)))) (rf/reg-fx :effects.wallet-connect/sign-typed-data - (fn [{:keys [password address data version on-success on-error]}] - (-> (wallet-connect-core/sign-typed-data version data address (security/safe-unmask-data password)) - (promesa/then wallet-connect-core/extract-native-call-signature) + (fn [{:keys [password address data version chain-id on-success on-error]}] + (-> (signing/eth-sign-typed-data (security/safe-unmask-data password) + address + data + chain-id + version) (promesa/then on-success) (promesa/catch on-error)))) diff --git a/src/status_im/contexts/wallet/wallet_connect/responding_events.cljs b/src/status_im/contexts/wallet/wallet_connect/responding_events.cljs index 6f0a5fed0f..838788576f 100644 --- a/src/status_im/contexts/wallet/wallet_connect/responding_events.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/responding_events.cljs @@ -12,14 +12,14 @@ method (wallet-connect-core/get-request-method event)] {:fx [(condp = method constants/wallet-connect-personal-sign-method - [:dispatch [:wallet-connect/respond-personal-sign password]] + [:dispatch [:wallet-connect/respond-sign-message password :personal-sign]] + + constants/wallet-connect-eth-sign-method + [:dispatch [:wallet-connect/respond-sign-message password :eth-sign]] constants/wallet-connect-eth-send-transaction-method [:dispatch [:wallet-connect/respond-send-transaction-data password]] - constants/wallet-connect-eth-sign-method - [:dispatch [:wallet-connect/respond-eth-sign password]] - constants/wallet-connect-eth-sign-transaction-method [:dispatch [:wallet-connect/respond-sign-transaction-data password]] @@ -30,35 +30,27 @@ [:dispatch [:wallet-connect/respond-sign-typed-data password :v4]])]}))) (rf/reg-event-fx - :wallet-connect/respond-eth-sign - (fn [{:keys [db]} [password]] - (let [{:keys [address raw-data]} (get db :wallet-connect/current-request)] - {:fx [[:effects.wallet-connect/sign-message - {:password password - :address address - :data raw-data - :on-error #(rf/dispatch [:wallet-connect/on-sign-error %]) - :on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]}))) - -(rf/reg-event-fx - :wallet-connect/respond-personal-sign - (fn [{:keys [db]} [password]] + :wallet-connect/respond-sign-message + (fn [{:keys [db]} [password rpc-method]] (let [{:keys [address raw-data]} (get db :wallet-connect/current-request)] {:fx [[:effects.wallet-connect/sign-message {:password password :address address :data raw-data + :rpc-method rpc-method :on-error #(rf/dispatch [:wallet-connect/on-sign-error %]) :on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]}))) (rf/reg-event-fx :wallet-connect/respond-sign-typed-data (fn [{:keys [db]} [password typed-data-version]] - (let [{:keys [address raw-data]} (get db :wallet-connect/current-request)] + (let [{:keys [address raw-data event]} (get db :wallet-connect/current-request) + chain-id (get-in event [:params :chainId])] {:fx [[:effects.wallet-connect/sign-typed-data {:password password :address address :data raw-data + :chain-id chain-id :version typed-data-version :on-error #(rf/dispatch [:wallet-connect/on-sign-error %]) :on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]}))) diff --git a/src/status_im/contexts/wallet/wallet_connect/rpc.cljs b/src/status_im/contexts/wallet/wallet_connect/rpc.cljs new file mode 100644 index 0000000000..3b1ce23afe --- /dev/null +++ b/src/status_im/contexts/wallet/wallet_connect/rpc.cljs @@ -0,0 +1,61 @@ +(ns status-im.contexts.wallet.wallet-connect.rpc + (:require [oops.core :as oops] + [promesa.core :as promesa] + [status-im.common.json-rpc.events :as rpc-events] + [status-im.constants :as constants] + [utils.hex :as hex] + [utils.transforms :as transforms])) + +(defn- call-rpc + "Helper to handle RPC calls to status-go as promises" + [method & args] + (promesa/create + (fn [p-resolve p-reject] + (rpc-events/call {:method method + :params args + :on-success p-resolve + :on-error p-reject + :js-response true})))) + +(defn wallet-build-transaction + [chain-id tx] + (promesa/let [res (call-rpc :wallet_buildTransaction chain-id tx)] + {:message-to-sign (oops/oget res :messageToSign) + :tx-args (oops/oget res :txArgs)})) + +(defn wallet-build-raw-transaction + [chain-id tx-args signature] + (-> (call-rpc "wallet_buildRawTransaction" + chain-id + (transforms/js-stringify tx-args 0) + signature) + (promesa/then #(oops/oget % "rawTx")))) + +(defn wallet-send-transaction-with-signature + [chain-id tx-args signature] + (call-rpc "wallet_sendTransactionWithSignature" + chain-id + constants/transaction-pending-type-wallet-connect-transfer + (transforms/js-stringify tx-args 0) + signature)) + +(defn wallet-sign-message + [message address password] + (-> (call-rpc "wallet_signMessage" + message + address + password) + (promesa/then hex/normalize-hex))) + +(defn wallet-hash-message-eip-191 + [message] + (call-rpc "wallet_hashMessageEIP191" message)) + +(defn wallet-safe-sign-typed-data + [data address password chain-id legacy?] + (call-rpc "wallet_safeSignTypedDataForDApps" + data + address + password + chain-id + legacy?)) diff --git a/src/status_im/contexts/wallet/wallet_connect/signing.cljs b/src/status_im/contexts/wallet/wallet_connect/signing.cljs new file mode 100644 index 0000000000..29ecec1021 --- /dev/null +++ b/src/status_im/contexts/wallet/wallet_connect/signing.cljs @@ -0,0 +1,32 @@ +(ns status-im.contexts.wallet.wallet-connect.signing + (:require [native-module.core :as native-module] + [promesa.core :as promesa] + [status-im.contexts.wallet.wallet-connect.core :as core] + [status-im.contexts.wallet.wallet-connect.rpc :as rpc] + [utils.hex :as hex] + [utils.transforms :as transforms])) + +(defn eth-sign + [password address data] + (-> {:data data + :account address + :password password} + transforms/clj->json + native-module/sign-message + (promesa/then core/extract-native-call-signature))) + +(defn personal-sign + [password address data] + (-> (rpc/wallet-hash-message-eip-191 data) + (promesa/then #(rpc/wallet-sign-message % address password)) + (promesa/then hex/prefix-hex))) + +(defn eth-sign-typed-data + [password address data chain-id-eip155 version] + (let [legacy? (= version :v1) + chain-id (core/eip155->chain-id chain-id-eip155)] + (rpc/wallet-safe-sign-typed-data data + address + password + chain-id + legacy?))) diff --git a/src/status_im/contexts/wallet/wallet_connect/transactions.cljs b/src/status_im/contexts/wallet/wallet_connect/transactions.cljs index 5e659c25b5..e23f196ecd 100644 --- a/src/status_im/contexts/wallet/wallet_connect/transactions.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/transactions.cljs @@ -1,23 +1,10 @@ (ns status-im.contexts.wallet.wallet-connect.transactions (:require [cljs-bean.core :as bean] [clojure.string :as string] - [oops.core :as oops] [promesa.core :as promesa] - [status-im.common.json-rpc.events :as rpc-events] - [status-im.constants :as constants] + [status-im.contexts.wallet.wallet-connect.rpc :as rpc] [utils.transforms :as transforms])) -(defn- call-rpc - "Helper to handle RPC calls to status-go as promises" - [method & args] - (promesa/create - (fn [p-resolve p-reject] - (rpc-events/call {:method method - :params (vec args) - :on-success p-resolve - :on-error p-reject - :js-response true})))) - (defn- strip-hex-prefix "Strips the extra 0 in hex value if present" [hex-value] @@ -46,53 +33,22 @@ bean/->js (transforms/js-stringify 0))) -(defn wallet-sign-message-rpc - [password address data] - (-> (call-rpc "wallet_signMessage" - data - address - password) - ;; NOTE: removing `0x`, as status-go expects the signature without it. - (promesa/then #(subs % 2)))) - -(defn- wallet-build-transaction-rpc - [chain-id tx] - (-> (call-rpc "wallet_buildTransaction" chain-id tx) - (promesa/then #(hash-map :message-to-sign (oops/oget % "messageToSign") - :tx-args (oops/oget % "txArgs"))))) - -(defn- wallet-build-raw-transaction-rpc - [chain-id tx-args signature] - (-> (call-rpc "wallet_buildRawTransaction" - chain-id - (transforms/js-stringify tx-args 0) - signature) - (promesa/then #(oops/oget % "rawTx")))) - -(defn- wallet-send-transaction-with-signature-rpc - [chain-id tx-args signature] - (call-rpc "wallet_sendTransactionWithSignature" - chain-id - constants/transaction-pending-type-wallet-connect-transfer - (transforms/js-stringify tx-args 0) - signature)) - (defn sign-transaction [password address tx chain-id] (promesa/let [formatted-tx (prepare-transaction-for-rpc tx) - {:keys [message-to-sign tx-args]} (wallet-build-transaction-rpc chain-id formatted-tx) - signature (wallet-sign-message-rpc password address message-to-sign) - raw-tx (wallet-build-raw-transaction-rpc chain-id tx-args signature)] + {:keys [message-to-sign tx-args]} (rpc/wallet-build-transaction chain-id formatted-tx) + signature (rpc/wallet-sign-message message-to-sign address password) + raw-tx (rpc/wallet-build-raw-transaction chain-id tx-args signature)] raw-tx)) (defn send-transaction [password address tx chain-id] (promesa/let [formatted-tx (prepare-transaction-for-rpc tx) - {:keys [message-to-sign tx-args]} (wallet-build-transaction-rpc chain-id formatted-tx) - signature (wallet-sign-message-rpc password address message-to-sign) - tx (wallet-send-transaction-with-signature-rpc chain-id + {:keys [message-to-sign tx-args]} (rpc/wallet-build-transaction chain-id formatted-tx) + signature (rpc/wallet-sign-message message-to-sign address password) + tx (rpc/wallet-send-transaction-with-signature chain-id tx-args signature)] tx)) diff --git a/src/status_im/subs/wallet/activities.cljs b/src/status_im/subs/wallet/activities.cljs index d636ceb88a..418680b12d 100644 --- a/src/status_im/subs/wallet/activities.cljs +++ b/src/status_im/subs/wallet/activities.cljs @@ -1,12 +1,12 @@ (ns status-im.subs.wallet.activities (:require - [legacy.status-im.utils.hex :as utils.hex] [native-module.core :as native-module] [quo.foundations.resources :as quo.resources] [quo.foundations.resources] [re-frame.core :as rf] [status-im.contexts.wallet.common.activity-tab.constants :as constants] [utils.datetime :as datetime] + [utils.hex :as utils.hex] [utils.money :as money])) (def precision 6) diff --git a/src/status_im/subs/wallet/wallet_connect.cljs b/src/status_im/subs/wallet/wallet_connect.cljs index 6530dca032..763bd8b8c1 100644 --- a/src/status_im/subs/wallet/wallet_connect.cljs +++ b/src/status_im/subs/wallet/wallet_connect.cljs @@ -50,7 +50,7 @@ :<- [:wallet-connect/current-request] (fn [request] (-> request - (get-in [:raw-data :params :chainId]) + (get-in [:event :params :chainId]) (wallet-connect-core/eip155->chain-id) (networks/get-network-details)))) diff --git a/src/utils/hex.cljs b/src/utils/hex.cljs new file mode 100644 index 0000000000..6892f35d28 --- /dev/null +++ b/src/utils/hex.cljs @@ -0,0 +1,27 @@ +(ns utils.hex + (:require + [clojure.string :as string] + [schema.core :as schema])) + +(defn normalize-hex + [hex] + (when hex + (string/lower-case (if (string/starts-with? hex "0x") + (subs hex 2) + hex)))) + +(schema/=> normalize-hex + [:=> + [:cat [:maybe :string]] + [:maybe :string]]) + +(defn prefix-hex + [hex] + (if (string/starts-with? hex "0x") + hex + (str "0x" hex))) + +(schema/=> prefix-hex + [:=> + [:cat :string] + :string])