From f51f52ec2734dc49410c72c0110fc537d8989ff0 Mon Sep 17 00:00:00 2001 From: Lungu Cristian Date: Wed, 3 Jul 2024 12:13:13 +0200 Subject: [PATCH] Fix WalletConnect transactions (#20608) * fix: sign-transaction Signed-off-by: Lungu Cristian * fix: sendTranasaction not working Signed-off-by: Lungu Cristian * ref: removing 0x from the signature Signed-off-by: Lungu Cristian * ref: tx data from responding to processing events Signed-off-by: Lungu Cristian * fix: request dapp subscription Signed-off-by: Lungu Cristian * fix: tx data bug Signed-off-by: Lungu Cristian --------- Signed-off-by: Lungu Cristian --- src/status_im/constants.cljs | 2 + .../wallet/wallet_connect/effects.cljs | 40 +++++--- .../wallet_connect/processing_events.cljs | 46 +++++---- .../wallet_connect/responding_events.cljs | 41 +++----- .../wallet/wallet_connect/transactions.cljs | 98 +++++++++++++++++++ src/status_im/subs/wallet/wallet_connect.cljs | 2 +- 6 files changed, 169 insertions(+), 60 deletions(-) create mode 100644 src/status_im/contexts/wallet/wallet_connect/transactions.cljs diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index e6d6ee0fad..6b78aa2f25 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -282,6 +282,8 @@ (def ^:const wallet-connect-session-proposal-event "session_proposal") (def ^:const wallet-connect-session-request-event "session_request") +(def ^:const transaction-pending-type-wallet-connect-transfer "WalletConnectTransfer") + (def ^:const dapp-permission-contact-code "contact-code") (def ^:const dapp-permission-web3 "web3") (def ^:const dapp-permission-qr-code "qr-code") diff --git a/src/status_im/contexts/wallet/wallet_connect/effects.cljs b/src/status_im/contexts/wallet/wallet_connect/effects.cljs index 36308ac914..1258487225 100644 --- a/src/status_im/contexts/wallet/wallet_connect/effects.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/effects.cljs @@ -1,15 +1,17 @@ (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] - [utils.i18n :as i18n] - [utils.security.core :as security] - [utils.transforms :as transforms])) + (: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.transactions :as transactions] + [utils.i18n :as i18n] + [utils.security.core :as security] + [utils.transforms :as transforms])) (rf/reg-fx :effects.wallet-connect/init @@ -92,10 +94,24 @@ (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) + (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) + (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 password) + (-> (wallet-connect-core/sign-typed-data version data address (security/safe-unmask-data password)) (promesa/then wallet-connect-core/extract-native-call-signature) (promesa/then on-success) (promesa/catch on-error)))) diff --git a/src/status_im/contexts/wallet/wallet_connect/processing_events.cljs b/src/status_im/contexts/wallet/wallet_connect/processing_events.cljs index 8e4cddd04b..2d5e7814a0 100644 --- a/src/status_im/contexts/wallet/wallet_connect/processing_events.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/processing_events.cljs @@ -1,5 +1,6 @@ (ns status-im.contexts.wallet.wallet-connect.processing-events - (:require [native-module.core :as native-module] + (:require [clojure.string :as string] + [native-module.core :as native-module] [re-frame.core :as rf] [status-im.constants :as constants] [status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core] @@ -45,7 +46,7 @@ {:db (update-in db [:wallet-connect/current-request] assoc - :address address + :address (string/lower-case address) :raw-data raw-data :display-data (or parsed-data raw-data))}))) @@ -57,36 +58,45 @@ {:db (update-in db [:wallet-connect/current-request] assoc - :address address + :address (string/lower-case address) :raw-data raw-data :display-data (or parsed-data raw-data))}))) (rf/reg-event-fx :wallet-connect/process-eth-send-transaction (fn [{:keys [db]}] - (let [event (wallet-connect-core/get-db-current-request-event db) - display-data (-> event - clj->js - (js/JSON.stringify nil 2)) - {:keys [to]} (-> event wallet-connect-core/get-request-params first)] + (let [event (wallet-connect-core/get-db-current-request-event db) + display-data (-> event + clj->js + (js/JSON.stringify nil 2)) + + {:keys [from] :as tx} (-> event wallet-connect-core/get-request-params first) + chain-id (-> event + (get-in [:params :chainId]) + wallet-connect-core/eip155->chain-id)] {:db (update-in db [:wallet-connect/current-request] assoc - :address to - :raw-data event + :address (string/lower-case from) + :raw-data tx + :chain-id chain-id :display-data display-data)}))) (rf/reg-event-fx :wallet-connect/process-eth-sign-transaction (fn [{:keys [db]}] - (let [event (wallet-connect-core/get-db-current-request-event db) - display-data (.stringify js/JSON (clj->js event) nil 2) - {:keys [to]} (-> event wallet-connect-core/get-request-params first)] + (let [event (wallet-connect-core/get-db-current-request-event db) + display-data (.stringify js/JSON (clj->js event) nil 2) + {:keys [from] :as tx} (-> event wallet-connect-core/get-request-params first) + chain-id (-> event + (get-in [:params :chainId]) + wallet-connect-core/eip155->chain-id)] {:db (update-in db [:wallet-connect/current-request] assoc - :address to - :raw-data event + :address (string/lower-case from) + :raw-data tx + :chain-id chain-id :display-data display-data)}))) (rf/reg-event-fx @@ -103,6 +113,6 @@ {:db (update-in db [:wallet-connect/current-request] assoc - :address address - :raw-data (or parsed-data raw-data) - :display-data parsed-data)}))) + :address (string/lower-case address) + :display-data (or parsed-data raw-data) + :raw-data raw-data)}))) 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 ee49f2e8e1..6a6422fba5 100644 --- a/src/status_im/contexts/wallet/wallet_connect/responding_events.cljs +++ b/src/status_im/contexts/wallet/wallet_connect/responding_events.cljs @@ -14,17 +14,13 @@ [:dispatch [:wallet-connect/respond-personal-sign password]] constants/wallet-connect-eth-send-transaction-method - [:dispatch - [:wallet-connect/respond-build-transaction - #(rf/dispatch [:wallet-connect/respond-send-transaction-data password %])]] + [: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-build-transaction - #(rf/dispatch [:wallet-connect/respond-sign-transaction-data password %])]] + [:dispatch [:wallet-connect/respond-sign-transaction-data password]] constants/wallet-connect-eth-sign-typed-method [:dispatch [:wallet-connect/respond-sign-typed-data password :v1]] @@ -67,40 +63,27 @@ :on-error #(rf/dispatch [:wallet-connect/on-sign-error %]) :on-success #(rf/dispatch [:wallet-connect/send-response %])}]]}))) -(rf/reg-event-fx - :wallet-connect/respond-build-transaction - (fn [{:keys [db]} [on-success]] - (let [{:keys [raw-data]} (get db :wallet-connect/current-request) - chain-id (-> raw-data - (get-in [:params :chainId]) - wallet-connect-core/eip155->chain-id)] - {:fx [[:json-rpc/call - [{:method "wallet_buildTransaction" - :params [chain-id (js/JSON.stringify raw-data)] - :on-success on-success - :on-error [:wallet-connect/on-sign-error]}]]]}))) - (rf/reg-event-fx :wallet-connect/respond-send-transaction-data - (fn [_ [password data]] - (let [{:keys [address] - message-to-sign :messageToSign} data] - {:fx [[:effects.wallet-connect/sign-message + (fn [{:keys [db]} [password]] + (let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)] + {:fx [[:effects.wallet-connect/send-transaction {:password password :address address - :data message-to-sign + :chain-id chain-id + :tx raw-data :on-error #(rf/dispatch [:wallet-connect/on-sign-error %]) :on-success #(rf/dispatch [:wallet-connect/send-response %])}]]}))) (rf/reg-event-fx :wallet-connect/respond-sign-transaction-data - (fn [_ [password data]] - (let [{:keys [address] - message-to-sign :messageToSign} data] - {:fx [[:effects.wallet-connect/sign-message + (fn [{:keys [db]} [password]] + (let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)] + {:fx [[:effects.wallet-connect/sign-transaction {:password password :address address - :data message-to-sign + :chain-id chain-id + :tx raw-data :on-error #(rf/dispatch [:wallet-connect/on-sign-error %]) :on-success #(rf/dispatch [:wallet-connect/send-response %])}]]}))) diff --git a/src/status_im/contexts/wallet/wallet_connect/transactions.cljs b/src/status_im/contexts/wallet/wallet_connect/transactions.cljs new file mode 100644 index 0000000000..5e659c25b5 --- /dev/null +++ b/src/status_im/contexts/wallet/wallet_connect/transactions.cljs @@ -0,0 +1,98 @@ +(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] + [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] + (let [formatted-hex (string/replace hex-value #"^0x0*" "0x")] + (if (= formatted-hex "0x") + "0x0" + formatted-hex))) + +(defn- format-tx-hex-values + "Due to how status-go expects hex values, we should remove the extra 0s in transaction hex values e.g. 0x0f -> 0xf" + [tx] + (let [tx-keys [:gasLimit :gas :gasPrice :nonce :value :maxFeePerGas :maxPriorityFeePerGas]] + (reduce (fn [acc tx-key] + (if (and (contains? tx tx-key) + (not (nil? (get tx tx-key)))) + (update acc tx-key strip-hex-prefix) + acc)) + tx + tx-keys))) + +(defn- prepare-transaction-for-rpc + "Formats the transaction and transforms it into a stringified JS object, ready to be passed to an RPC call." + [tx] + (-> tx + format-tx-hex-values + 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)] + 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 + tx-args + signature)] + tx)) diff --git a/src/status_im/subs/wallet/wallet_connect.cljs b/src/status_im/subs/wallet/wallet_connect.cljs index 752e8ced79..6530dca032 100644 --- a/src/status_im/subs/wallet/wallet_connect.cljs +++ b/src/status_im/subs/wallet/wallet_connect.cljs @@ -39,7 +39,7 @@ :<- [:wallet-connect/current-request] :<- [:wallet-connect/pairings] (fn [[request pairings]] - (let [dapp-url (get-in request [:raw-data :verifyContext :verified :origin])] + (let [dapp-url (get-in request [:event :verifyContext :verified :origin])] (->> pairings (filter (fn [pairing] (= dapp-url (get-in pairing [:peerMetadata :url]))))