Wallet Connect message signing (#20693)

* feat: updated signing endpoints and refactor

6e056348...e8aec741

* fix: address review comments
This commit is contained in:
Lungu Cristian 2024-07-16 14:36:16 +03:00 committed by GitHub
parent bd9e440839
commit 588692e0eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 171 additions and 110 deletions

View File

@ -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

View File

@ -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))))

View File

@ -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

View File

@ -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))))

View File

@ -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 %}])}]]})))

View File

@ -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?))

View File

@ -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?)))

View File

@ -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))

View File

@ -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)

View File

@ -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))))

27
src/utils/hex.cljs Normal file
View File

@ -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])