From 49a41f4787bca468e5cef34b94dc98528f188c9a Mon Sep 17 00:00:00 2001 From: Ulises Manuel <90291778+ulisesmac@users.noreply.github.com> Date: Fri, 24 May 2024 10:03:02 -0600 Subject: [PATCH] [#19232] - Fix derivation path generation and keypair creation (#19531) * Add more default dependencies to slide button * Fix wallet account creation: derivation paths and keypairs --- .../standard_auth/slide_button/view.cljs | 2 +- .../confirm_address/view.cljs | 3 +- .../add_account/create_account/events.cljs | 125 +++++-- .../create_account/events_test.cljs | 68 ++-- .../create_account/key_pair_name/view.cljs | 4 +- .../backup_recovery_phrase/view.cljs | 2 +- .../new_keypair/confirm_backup/view.cljs | 4 +- .../add_account/create_account/utils.cljs | 46 ++- .../add_account/create_account/view.cljs | 322 +++++++++++------- src/status_im/contexts/wallet/effects.cljs | 21 +- src/status_im/contexts/wallet/events.cljs | 58 +--- src/status_im/subs/wallet/wallet.cljs | 24 ++ 12 files changed, 446 insertions(+), 233 deletions(-) diff --git a/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs b/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs index 350a10458b..dd5cb2aa7c 100644 --- a/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs +++ b/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs @@ -24,7 +24,7 @@ :on-auth-success on-auth-success :on-auth-fail on-auth-fail :auth-button-label auth-button-label}])) - (into [] (concat [theme] dependencies)))] + (vec (conj dependencies on-auth-success on-auth-fail)))] [quo/slide-button {:container-style container-style :size size diff --git a/src/status_im/contexts/wallet/add_account/add_address_to_watch/confirm_address/view.cljs b/src/status_im/contexts/wallet/add_account/add_address_to_watch/confirm_address/view.cljs index 23db69b630..2475aa87bc 100644 --- a/src/status_im/contexts/wallet/add_account/add_address_to_watch/confirm_address/view.cljs +++ b/src/status_im/contexts/wallet/add_account/add_address_to_watch/confirm_address/view.cljs @@ -38,8 +38,7 @@ :disabled? (string/blank? @account-name) :accessibility-label :confirm-button-label :on-press #(rf/dispatch [:wallet/add-account - {:sha3-pwd nil - :type :watch + {:type :watch :account-name @account-name :emoji @account-emoji :color @account-color} diff --git a/src/status_im/contexts/wallet/add_account/create_account/events.cljs b/src/status_im/contexts/wallet/add_account/create_account/events.cljs index 557da86acd..5673cf6a64 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/events.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/events.cljs @@ -1,6 +1,10 @@ (ns status-im.contexts.wallet.add-account.create-account.events (:require [camel-snake-kebab.extras :as cske] + [clojure.string :as string] + [status-im.constants :as constants] + [status-im.contexts.wallet.add-account.create-account.utils :as create-account.utils] [status-im.contexts.wallet.data-store :as data-store] + [taoensso.timbre :as log] [utils.re-frame :as rf] [utils.security.core :as security] [utils.transforms :as transforms])) @@ -22,14 +26,18 @@ (rf/reg-event-fx :wallet/confirm-account-origin confirm-account-origin) -(defn store-seed-phrase +(defn store-new-seed-phrase [{:keys [db]} [{:keys [seed-phrase random-phrase]}]] - {:db (-> db - (assoc-in [:wallet :ui :create-account :seed-phrase] seed-phrase) - (assoc-in [:wallet :ui :create-account :random-phrase] random-phrase)) - :fx [[:dispatch-later [{:ms 20 :dispatch [:navigate-to :screen/wallet.confirm-backup]}]]]}) + {:db (update-in db + [:wallet :ui :create-account :new-keypair] + assoc + :seed-phrase seed-phrase + :random-phrase random-phrase) + :fx [[:dispatch-later + [{:ms 20 + :dispatch [:navigate-to :screen/wallet.confirm-backup]}]]]}) -(rf/reg-event-fx :wallet/store-seed-phrase store-seed-phrase) +(rf/reg-event-fx :wallet/store-new-seed-phrase store-new-seed-phrase) (defn seed-phrase-validated [{:keys [db]} [seed-phrase]] @@ -43,33 +51,45 @@ {:fx [[:multiaccount/validate-mnemonic [seed-phrase (fn [mnemonic key-uid] - (rf/dispatch [:wallet/seed-phrase-validated - mnemonic key-uid])) + (rf/dispatch [:wallet/seed-phrase-validated mnemonic key-uid])) on-error]]]}) (rf/reg-event-fx :wallet/seed-phrase-entered seed-phrase-entered) -(defn new-keypair-created - [{:keys [db]} [{:keys [new-keypair]}]] - {:db (assoc-in db [:wallet :ui :create-account :new-keypair] new-keypair) - :fx [[:dispatch [:navigate-back-to :screen/wallet.create-account]]]}) +(defn store-account-generated + [{:keys [db]} [{:keys [new-account-data keypair-name]}]] + (let [new-account (update new-account-data :mnemonic security/mask-data)] + {:db (-> db + (update-in [:wallet :ui :create-account :new-keypair] + assoc + :new-account-data new-account + :keypair-name keypair-name) + (update-in [:wallet :ui :create-account :new-keypair] + dissoc + :seed-phrase + :random-phrase)) + :fx [[:dispatch [:navigate-back-to :screen/wallet.create-account]]]})) -(rf/reg-event-fx :wallet/new-keypair-created new-keypair-created) +(rf/reg-event-fx :wallet/store-account-generated store-account-generated) -(defn new-keypair-continue +(defn generate-account-for-keypair [{:keys [db]} [{:keys [keypair-name]}]] - (let [seed-phrase (get-in db [:wallet :ui :create-account :seed-phrase])] + (let [seed-phrase (-> db :wallet :ui :create-account :new-keypair :seed-phrase)] {:fx [[:effects.wallet/create-account-from-mnemonic - {:seed-phrase (security/safe-unmask-data seed-phrase) - :keypair-name keypair-name}]]})) + {:mnemonic-phrase (security/safe-unmask-data seed-phrase) + :paths [constants/path-default-wallet] + :on-success (fn [new-account-data] + (rf/dispatch [:wallet/store-account-generated + {:new-account-data new-account-data + :keypair-name keypair-name}]))}]]})) -(rf/reg-event-fx :wallet/new-keypair-continue new-keypair-continue) +(rf/reg-event-fx :wallet/generate-account-for-keypair generate-account-for-keypair) -(defn clear-new-keypair +(defn clear-create-account-data [{:keys [db]}] {:db (update-in db [:wallet :ui :create-account] dissoc :new-keypair)}) -(rf/reg-event-fx :wallet/clear-new-keypair clear-new-keypair) +(rf/reg-event-fx :wallet/clear-create-account clear-create-account-data) (defn get-derived-addresses [{:keys [db]} [{:keys [password derived-from paths]}]] @@ -80,6 +100,21 @@ (rf/reg-event-fx :wallet/get-derived-addresses get-derived-addresses) +(rf/reg-event-fx + :wallet/next-derivation-path + (fn [_ [{:keys [on-success keypair-uid]}]] + {:fx [[:json-rpc/call + [{:method "accounts_resolveSuggestedPathForKeypair" + :params [keypair-uid] + :on-success on-success + :on-error (fn [error] + (log/error + "Failed to resolve next path derivation path" + {:event :wallet/next-derivation-path + :method "accounts_resolveSuggestedPathForKeypair" + :error error + :params keypair-uid}))}]]]})) + (defn get-derived-addresses-success [{:keys [db]} [response]] (let [derived-address (first response)] @@ -105,3 +140,53 @@ :wallet/clear-private-key-data (fn [{:keys [db]} _] {:db (update-in db [:wallet :ui :create-account] dissoc :private-key :public-address)})) + +(rf/reg-event-fx + :wallet/create-keypair-with-account + (fn [{db :db} [password account-preferences]] + (let [{:keys [keypair-name + new-account-data]} (-> db :wallet :ui :create-account :new-keypair) + keypair-with-account (create-account.utils/prepare-new-account + {:keypair-name keypair-name + :account-data new-account-data + :account-preferences account-preferences}) + new-address (some-> new-account-data + (create-account.utils/first-derived-account) + (:address) + (string/lower-case)) + unmasked-password (security/safe-unmask-data password)] + {:fx [[:json-rpc/call + [{:method "accounts_addKeypair" + :params [unmasked-password keypair-with-account] + :on-success [:wallet/add-account-success new-address] + :on-error #(log/error "Failed to add Keypair and create account" %)}]]]}))) + +(defn import-and-create-keypair-with-account + [{db :db} [{:keys [password account-preferences]}]] + (let [account-data (-> db :wallet :ui :create-account :new-keypair :new-account-data) + unmasked-mnemonic (security/safe-unmask-data (:mnemonic account-data)) + unmasked-password (security/safe-unmask-data password)] + {:fx [[:json-rpc/call + [{:method "accounts_importMnemonic" + :params [unmasked-mnemonic unmasked-password] + :on-success #(rf/dispatch + [:wallet/create-keypair-with-account password account-preferences])}]]]})) + +(rf/reg-event-fx :wallet/import-and-create-keypair-with-account import-and-create-keypair-with-account) + +(rf/reg-event-fx + :wallet/derive-address-and-add-account + (fn [_ [{:keys [password derived-from-address derivation-path account-preferences]}]] + {:fx [[:json-rpc/call + [{:method "wallet_getDerivedAddresses" + :params [(security/safe-unmask-data password) + derived-from-address + [derivation-path]] + :on-success (fn [[derived-account]] + (rf/dispatch [:wallet/add-account + (assoc account-preferences :password password) + derived-account])) + :on-error #(log/info "Failed to get derived addresses" + derived-from-address + %)}]]]})) + diff --git a/src/status_im/contexts/wallet/add_account/create_account/events_test.cljs b/src/status_im/contexts/wallet/add_account/create_account/events_test.cljs index 3e124d8686..0928737157 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/events_test.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/events_test.cljs @@ -1,8 +1,10 @@ (ns status-im.contexts.wallet.add-account.create-account.events-test (:require [cljs.test :refer-macros [deftest is]] - matcher-combinators.test - [status-im.contexts.wallet.add-account.create-account.events :as events])) + [matcher-combinators.test] + [status-im.constants :as constants] + [status-im.contexts.wallet.add-account.create-account.events :as events] + [utils.security.core :as security])) (deftest confirm-account-origin (let [db {:wallet {:ui {:create-account {}}}} @@ -15,32 +17,60 @@ (deftest store-seed-phrase (let [db {} props [{:seed-phrase "test-secret" :random-phrase "random-test"}] - expected-db {:wallet {:ui {:create-account {:seed-phrase "test-secret" - :random-phrase "random-test"}}}} - effects (events/store-seed-phrase {:db db} props) + expected-db {:wallet {:ui {:create-account {:new-keypair {:seed-phrase "test-secret" + :random-phrase "random-test"}}}}} + effects (events/store-new-seed-phrase {:db db} props) result-db (:db effects)] (is (match? result-db expected-db)))) -(deftest new-keypair-created - (let [db {} - props [{:new-keypair "test-keypair"}] - expected-db {:wallet {:ui {:create-account {:new-keypair "test-keypair"}}}} - effects (events/new-keypair-created {:db db} props) - result-db (:db effects)] - (is (match? result-db expected-db)))) +(deftest store-account-generated + (let [db {:wallet {:ui {:create-account + {:new-keypair {:seed-phrase "test-secret" + :random-phrase "random-test"}}}}} + mnemonic "my mnemonic" + masked-mnemonic (security/mask-data mnemonic) + props [{:new-account-data {"test" "data" + :mnemonic mnemonic} + :keypair-name "new-keypair-name"}] + expected-db {:wallet {:ui {:create-account + {:new-keypair + {:new-account-data {"test" "data" + :mnemonic masked-mnemonic} + :keypair-name "new-keypair-name"}}}}} + effects (events/store-account-generated {:db db} props) + result-db (:db effects) + remove-mnemonic #(update-in % + [:wallet :ui :create-account :new-keypair :new-account-data] + dissoc + :mnemonic) + unmask-mnemonic #(-> % + :wallet + :ui + :create-account + :new-keypair + :new-account-data + :mnemonic + security/safe-unmask-data)] + (is (= (remove-mnemonic result-db) (remove-mnemonic expected-db))) + (is (= (unmask-mnemonic result-db) (unmask-mnemonic expected-db))))) -(deftest new-keypair-continue - (let [db {:wallet {:ui {:create-account {:seed-phrase "test-secret"}}}} + +(deftest generate-account-for-keypair + (let [db {:wallet {:ui {:create-account {:new-keypair {:seed-phrase "test-secret"}}}}} props [{:keypair-name "test-keypair"}] expected-effects [[:effects.wallet/create-account-from-mnemonic - {:seed-phrase "test-secret" :keypair-name "test-keypair"}]] - effects (events/new-keypair-continue {:db db} props)] - (is (match? effects {:fx expected-effects})))) + {:mnemonic-phrase "test-secret" + :paths [constants/path-default-wallet]}]] + effects (events/generate-account-for-keypair {:db db} props)] + (is (match? + (update-in effects [:fx 0 1] dissoc :on-success) + {:fx expected-effects})) + (is (some? (get-in effects [:fx 0 1 :on-success]))))) -(deftest clear-new-keypair +(deftest clear-create-account-data (let [db {:wallet {:ui {:create-account {:new-keypair "test-keypair"}}}} expected-db {:wallet {:ui {:create-account {}}}} - effects (events/clear-new-keypair {:db db})] + effects (events/clear-create-account-data {:db db})] (is (match? (:db effects) expected-db)))) (deftest get-derived-addresses-test diff --git a/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs index 254b9a2158..18ffbfe03e 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/key_pair_name/view.cljs @@ -52,8 +52,8 @@ :import-private-key (not-implemented/alert) - :new-key-pair - (rf/dispatch [:wallet/new-keypair-continue + :new-keypair + (rf/dispatch [:wallet/generate-account-for-keypair {:keypair-name key-pair-name}]) (js/alert "Unknown workflow"))) [workflow key-pair-name]) diff --git a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/backup_recovery_phrase/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/backup_recovery_phrase/view.cljs index 450366d251..7d7409cd29 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/backup_recovery_phrase/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/backup_recovery_phrase/view.cljs @@ -108,7 +108,7 @@ :button-one-label (i18n/label :t/i-have-written) :button-one-props {:disabled? (some false? (vals @checked?)) :customization-color customization-color - :on-press #(rf/dispatch [:wallet/store-seed-phrase + :on-press #(rf/dispatch [:wallet/store-new-seed-phrase {:seed-phrase (security/mask-data @seed-phrase) :random-phrase @random-phrase}])}}] diff --git a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/confirm_backup/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/confirm_backup/view.cljs index e21febe996..70111dddcf 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/new_keypair/confirm_backup/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/new_keypair/confirm_backup/view.cljs @@ -65,7 +65,7 @@ quiz-index (reagent/atom 0) incorrect-count (reagent/atom 0) show-error? (reagent/atom false) - {:keys [seed-phrase random-phrase]} (rf/sub [:wallet/create-account]) + {:keys [seed-phrase random-phrase]} (rf/sub [:wallet/create-account-new-keypair]) unmasked-seed-phrase (security/safe-unmask-data seed-phrase)] (fn [] (let [current-word-index (get random-indices @@ -82,7 +82,7 @@ (when (= @quiz-index questions-count) (rf/dispatch [:navigate-to :screen/wallet.keypair-name - {:workflow :new-key-pair}]))) + {:workflow :new-keypair}]))) (do (when (> @incorrect-count 0) (rf/dispatch [:show-bottom-sheet diff --git a/src/status_im/contexts/wallet/add_account/create_account/utils.cljs b/src/status_im/contexts/wallet/add_account/create_account/utils.cljs index 844ed6a6af..121cbc34a6 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/utils.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/utils.cljs @@ -1,19 +1,29 @@ -(ns status-im.contexts.wallet.add-account.create-account.utils) +(ns status-im.contexts.wallet.add-account.create-account.utils + (:require [status-im.constants :as constants])) -(defn prepare-new-keypair - [{:keys [new-keypair address account-name account-color emoji derivation-path]}] - (assoc new-keypair - :name (:keypair-name new-keypair) - :key-uid (:keyUid new-keypair) - :type :seed - :derived-from address - :accounts [{:keypair-name (:keypair-name new-keypair) - :key-uid (:keyUid new-keypair) - :seed-phrase (:mnemonic new-keypair) - :public-key (:publicKey new-keypair) - :name account-name - :type :seed - :emoji emoji - :colorID account-color - :path derivation-path - :address (:address new-keypair)}])) +(defn first-derived-account + [account-data] + (-> account-data :derived first val)) + +(defn prepare-new-account + [{keypair-name :keypair-name + {:keys [keyUid address] :as account} :account-data + {:keys [account-name color emoji]} :account-preferences}] + (let [account-to-create (first-derived-account account) + account-config {:address (:address account-to-create) + :key-uid keyUid + :wallet false + :chat false + :type :seed + :path constants/path-default-wallet + :public-key (:publicKey account-to-create) + :name account-name + :emoji emoji + :colorID color + :hidden false}] + {:key-uid keyUid + :name keypair-name + :type :seed + :derived-from address + :last-used-derivation-index 0 + :accounts [account-config]})) diff --git a/src/status_im/contexts/wallet/add_account/create_account/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/view.cljs index dbb09737c0..a0661c54e4 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/view.cljs @@ -12,14 +12,11 @@ [status-im.common.standard-authentication.core :as standard-auth] [status-im.constants :as constants] [status-im.contexts.wallet.add-account.create-account.style :as style] - [status-im.contexts.wallet.add-account.create-account.utils :as create-account.utils] - [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.sheets.account-origin.view :as account-origin] [status-im.feature-flags :as ff] [utils.i18n :as i18n] [utils.re-frame :as rf] [utils.responsiveness :as responsiveness] - [utils.security.core :as security] [utils.string])) (defn- get-keypair-data @@ -57,107 +54,47 @@ :description :text :description-props {:text formatted-path}}])) -(defn view +(defn- avatar + [{:keys [account-color emoji on-select-emoji]}] + [rn/view {:style style/account-avatar-container} + [quo/account-avatar + {:customization-color account-color + :size 80 + :emoji emoji + :type :default}] + [quo/button + {:size 32 + :type :grey + :background :photo + :icon-only? true + :on-press #(rf/dispatch [:emoji-picker/open {:on-select on-select-emoji}]) + :container-style style/reaction-button-container} + :i/reaction]]) + +(defn- input [_] - (let [top (safe-area/get-top) - bottom (safe-area/get-bottom) - {window-width :width} (rn/get-window) - account-color (reagent/atom (rand-nth colors/account-colors)) - emoji (reagent/atom (emoji-picker.utils/random-emoji)) - account-name (reagent/atom "") - on-change-text #(reset! account-name %) - show-account-origin #(rf/dispatch [:show-bottom-sheet - {:content account-origin/view}])] - (fn [] - (let [theme (quo.theme/use-theme) - number-of-accounts (count (rf/sub - [:wallet/accounts-without-watched-accounts])) - {:keys [address customization-color]} (rf/sub [:profile/profile]) - {:keys [new-keypair]} (rf/sub [:wallet/create-account]) - keypairs (rf/sub [:wallet/keypairs]) - selected-keypair-uid (rf/sub [:wallet/selected-keypair-uid]) - placeholder (i18n/label :t/default-account-placeholder) - derivation-path (utils/get-derivation-path - number-of-accounts) - keypair (some #(when (= (:key-uid %) selected-keypair-uid) - %) - keypairs) - primary-keypair? (= selected-keypair-uid (:key-uid (first keypairs))) - create-new-keypair-account #(rf/dispatch - [:wallet/add-keypair-and-create-account - {:sha3-pwd (security/safe-unmask-data %) - :new-keypair - (create-account.utils/prepare-new-keypair - {:new-keypair new-keypair - :address address - :account-name @account-name - :account-color @account-color - :emoji @emoji - :derivation-path - derivation-path})}]) - create-existing-keypair-account #(rf/dispatch [:wallet/derive-address-and-add-account - {:sha3-pwd (security/safe-unmask-data %) - :emoji @emoji - :color @account-color - :path derivation-path - :account-name @account-name}]) - keypair-title (or (:keypair-name new-keypair) - (if primary-keypair? - (i18n/label :t/keypair-title - {:name (:name keypair)}) - (:name keypair)))] - (rn/use-unmount #(rf/dispatch [:wallet/clear-new-keypair])) - [floating-button-page/view - {:gradient-cover? true - :footer-container-padding 0 - :header-container-style {:padding-top top} - :customization-color @account-color - :header [quo/page-nav - {:type :no-title - :background :blur - :right-side [{:icon-name :i/info - :on-press show-account-origin}] - :icon-name :i/close - :on-press #(rf/dispatch [:navigate-back])}] - :footer [standard-auth/slide-button - {:size :size-48 - :track-text (i18n/label :t/slide-to-create-account) - :customization-color @account-color - :on-auth-success (fn [password] - (if new-keypair - (create-new-keypair-account password) - (create-existing-keypair-account - password))) - :auth-button-label (i18n/label :t/confirm) - :disabled? (empty? @account-name) - :container-style (style/slide-button-container bottom) - :dependencies [new-keypair]}]} - [rn/view {:style style/account-avatar-container} - [quo/account-avatar - {:customization-color @account-color - :size 80 - :emoji @emoji - :type :default}] - [quo/button - {:size 32 - :type :grey - :background :photo - :icon-only? true - :on-press #(rf/dispatch [:emoji-picker/open - {:on-select (fn [selected-emoji] - (reset! emoji selected-emoji))}]) - :container-style style/reaction-button-container} - :i/reaction]] - [quo/title-input - {:customization-color @account-color - :placeholder placeholder - :on-change-text on-change-text - :max-length constants/wallet-account-name-max-length - :blur? true - :disabled? false - :default-value @account-name - :container-style style/title-input-container}] - [quo/divider-line] + (let [placeholder (i18n/label :t/default-account-placeholder)] + (fn [{:keys [account-color account-name on-change-text]}] + [quo/title-input + {:customization-color account-color + :placeholder placeholder + :on-change-text on-change-text + :max-length constants/wallet-account-name-max-length + :blur? true + :disabled? false + :default-value account-name + :container-style style/title-input-container}]))) + +(defn- color-picker + [_] + (let [{window-width :width} (rn/get-window) + color-picker-style {:padding-vertical 12 + :padding-left (responsiveness/iphone-11-Pro-20-pixel-from-width + window-width)}] + (fn [{:keys [account-color set-account-color]}] + [:<> + [quo/divider-line] + (let [theme (quo.theme/use-theme)] [rn/view {:style style/color-picker-container} [quo/text {:size :paragraph-2 @@ -165,17 +102,168 @@ :style (style/color-label theme)} (i18n/label :t/colour)] [quo/color-picker - {:default-selected @account-color - :on-change #(reset! account-color %) - :container-style {:padding-vertical 12 - :padding-left (responsiveness/iphone-11-Pro-20-pixel-from-width - window-width)}}]] - [quo/divider-line] - [quo/category - {:list-type :settings - :label (i18n/label :t/origin) - :data (get-keypair-data {:title keypair-title - :primary-keypair? primary-keypair? - :new-keypair? (boolean new-keypair) - :derivation-path derivation-path - :customization-color customization-color})}]])))) + {:default-selected account-color + :on-change set-account-color + :container-style color-picker-style}]])]))) + +(defn- new-account-origin + [{:keys [keypair-title derivation-path customization-color]}] + (let [{keypair-name :name} (rf/sub [:wallet/selected-keypair]) + primary? (rf/sub [:wallet/selected-primary-keypair?]) + keypair-name (or keypair-title + (if primary? + (i18n/label :t/keypair-title {:name keypair-name}) + keypair-name))] + [:<> + [quo/divider-line] + [quo/category + {:list-type :settings + :label (i18n/label :t/origin) + :data (get-keypair-data {:primary-keypair? primary? + :title keypair-name + :derivation-path derivation-path + :customization-color customization-color})}]])) + +(defn- floating-button + [_ & _] + (let [top (safe-area/get-top) + bottom (safe-area/get-bottom) + header [quo/page-nav + {:type :no-title + :background :blur + :right-side [{:icon-name :i/info + :on-press #(rf/dispatch [:show-bottom-sheet + {:content account-origin/view}])}] + :icon-name :i/close + :on-press #(rf/dispatch [:navigate-back])}]] + (fn [{:keys [slide-button-props account-color]} & children] + (into + [floating-button-page/view + {:gradient-cover? true + :footer-container-padding 0 + :header-container-style {:padding-top top} + :customization-color account-color + :header header + :footer [standard-auth/slide-button + (assoc slide-button-props + :size :size-48 + :track-text (i18n/label :t/slide-to-create-account) + :customization-color account-color + :auth-button-label (i18n/label :t/confirm) + :container-style (style/slide-button-container bottom))]}] + children)))) + +(defn add-new-keypair-variant + [{:keys [on-change-text set-account-color set-emoji] + {:keys [account-name account-color emoji]} + :state}] + (let [on-auth-success (fn [password] + (rf/dispatch + [:wallet/import-and-create-keypair-with-account + {:password password + :account-preferences {:account-name @account-name + :color @account-color + :emoji @emoji}}]))] + (fn [{:keys [customization-color keypair-name]}] + (let [{:keys [new-account-data]} (rf/sub [:wallet/create-account-new-keypair])] + [floating-button + {:account-color @account-color + :slide-button-props {:on-auth-success on-auth-success + :disabled? (empty? @account-name) + :dependencies [new-account-data]}} + [avatar + {:account-color @account-color + :emoji @emoji + :on-select-emoji set-emoji}] + [input + {:account-color @account-color + :account-name @account-name + :on-change-text on-change-text}] + [color-picker + {:account-color @account-color + :set-account-color set-account-color}] + [new-account-origin + {:derivation-path constants/path-default-wallet + :customization-color customization-color + :keypair-title keypair-name}]])))) + +(defn derive-account-variant + [{:keys [on-change-text set-account-color set-emoji] + {:keys [account-name account-color emoji]} + :state}] + (let [derivation-path (reagent/atom "") + set-derivation-path #(reset! derivation-path %)] + (fn [{:keys [customization-color]}] + (let [{:keys [derived-from + key-uid]} (rf/sub [:wallet/selected-keypair]) + on-auth-success (rn/use-callback + (fn [password] + (let [preferences {:account-name @account-name + :color @account-color + :emoji @emoji}] + (rf/dispatch + [:wallet/derive-address-and-add-account + {:password password + :derived-from-address derived-from + :derivation-path @derivation-path + :account-preferences preferences}]))) + [derived-from])] + (rn/use-effect + #(rf/dispatch + [:wallet/next-derivation-path + {:on-success set-derivation-path + :keypair-uid key-uid}]) + [key-uid]) + + [floating-button + {:account-color @account-color + :slide-button-props {:on-auth-success on-auth-success + :disabled? (or (empty? @account-name) + (= "" @derivation-path))}} + [avatar + {:account-color @account-color + :emoji @emoji + :on-select-emoji set-emoji}] + [input + {:account-color @account-color + :account-name @account-name + :on-change-text on-change-text}] + [color-picker + {:account-color @account-color + :set-account-color set-account-color}] + [new-account-origin + {:derivation-path @derivation-path + :customization-color customization-color}]])))) + +(defn view + [_] + (let [account-name (reagent/atom "") + account-color (reagent/atom (rand-nth colors/account-colors)) + emoji (reagent/atom (emoji-picker.utils/random-emoji)) + on-change-text #(reset! account-name %) + set-account-color #(reset! account-color %) + set-emoji #(reset! emoji %) + state {:account-name account-name + :account-color account-color + :emoji emoji}] + (fn [] + (let [customization-color (rf/sub [:profile/customization-color]) + ;; Having a keypair means the user is importing it or creating it. + {:keys [keypair-name]} (rf/sub [:wallet/create-account-new-keypair])] + + (rn/use-unmount #(rf/dispatch [:wallet/clear-create-account])) + + (if keypair-name + [add-new-keypair-variant + {:customization-color customization-color + :on-change-text on-change-text + :set-account-color set-account-color + :set-emoji set-emoji + :state state + :keypair-name keypair-name}] + [derive-account-variant + {:customization-color customization-color + :on-change-text on-change-text + :set-account-color set-account-color + :set-emoji set-emoji + :state state}]))))) diff --git a/src/status_im/contexts/wallet/effects.cljs b/src/status_im/contexts/wallet/effects.cljs index 9da556fbc2..f11b84d9d4 100644 --- a/src/status_im/contexts/wallet/effects.cljs +++ b/src/status_im/contexts/wallet/effects.cljs @@ -2,15 +2,18 @@ (:require [clojure.string :as string] [native-module.core :as native-module] - [re-frame.core :as rf])) + [re-frame.core :as rf] + [taoensso.timbre :as log])) (rf/reg-fx :effects.wallet/create-account-from-mnemonic - (fn [{:keys [seed-phrase keypair-name]}] - (native-module/create-account-from-mnemonic - {:MnemonicPhrase (if (string? seed-phrase) - seed-phrase - (string/join " " seed-phrase))} - (fn [new-keypair] - (rf/dispatch [:wallet/new-keypair-created - {:new-keypair (assoc new-keypair :keypair-name keypair-name)}]))))) + (fn [{:keys [mnemonic-phrase paths on-success] + :or {paths []}}] + (let [phrase (condp #(%1 %2) mnemonic-phrase + string? mnemonic-phrase + coll? (string/join " " mnemonic-phrase) + (log/error "Unexpected value " mnemonic-phrase))] + (native-module/create-account-from-mnemonic + {:MnemonicPhrase phrase + :paths paths} + on-success)))) diff --git a/src/status_im/contexts/wallet/events.cljs b/src/status_im/contexts/wallet/events.cljs index 18549665cc..691fd88934 100644 --- a/src/status_im/contexts/wallet/events.cljs +++ b/src/status_im/contexts/wallet/events.cljs @@ -16,17 +16,18 @@ [utils.i18n :as i18n] [utils.number] [utils.re-frame :as rf] + [utils.security.core :as security] [utils.transforms :as transforms])) (rf/reg-event-fx :wallet/show-account-created-toast (fn [{:keys [db]} [address]] - (let [account (get-in db [:wallet :accounts address])] + (let [account-name (get-in db [:wallet :accounts address :name])] {:db (update db :wallet dissoc :navigate-to-account :new-account?) :fx [[:dispatch [:toasts/upsert {:id :new-wallet-account-created :type :positive - :text (i18n/label :t/account-created {:name (:name account)})}]]]}))) + :text (i18n/label :t/account-created {:name account-name})}]]]}))) (rf/reg-event-fx :wallet/navigate-to-account (fn [{:keys [db]} [address]] @@ -173,30 +174,22 @@ [{:keys [db]}] {:db (update-in db [:wallet :ui] dissoc :scanned-address)}) -(rf/reg-event-fx :wallet/create-derived-addresses - (fn [{:keys [db]} [{:keys [sha3-pwd path]} on-success]] - (let [{:keys [address]} (:profile/profile db)] - {:fx [[:json-rpc/call - [{:method "wallet_getDerivedAddresses" - :params [sha3-pwd address [path]] - :on-success on-success - :on-error #(log/info "failed to derive address " %)}]]]}))) - (rf/reg-event-fx :wallet/add-account-success (fn [{:keys [db]} [address]] - {:db (update db - :wallet assoc - :navigate-to-account address - :new-account? true) + {:db (-> db + (assoc-in [:wallet :navigate-to-account] address) + (assoc-in [:wallet :new-account?] true)) :fx [[:dispatch [:wallet/get-accounts]] [:dispatch [:wallet/get-keypairs]] - [:dispatch [:wallet/clear-new-keypair]]]})) + [:dispatch [:wallet/clear-create-account]]]})) (rf/reg-event-fx :wallet/add-account (fn [{:keys [db]} - [{:keys [sha3-pwd emoji account-name color type] :or {type :generated}} - {:keys [public-key address path]}]] - (let [lowercase-address (if address (string/lower-case address) address) + [{:keys [password account-name emoji color type] + :or {type :generated}} + {:keys [public-key address path] :as _derived-account}]] + (let [lowercase-address (some-> address + (string/lower-case)) key-uid (get-in db [:wallet :ui :create-account :selected-keypair-uid]) account-config {:key-uid (when (= type :generated) key-uid) :wallet false @@ -210,30 +203,11 @@ :colorID color}] {:fx [[:json-rpc/call [{:method "accounts_addAccount" - :params [(when (= type :generated) sha3-pwd) account-config] + :params [(when (= type :generated) + (security/safe-unmask-data password)) + account-config] :on-success [:wallet/add-account-success lowercase-address] - :on-error #(log/info "failed to create account " %)}]]]}))) - -(rf/reg-event-fx - :wallet/derive-address-and-add-account - (fn [_ [account-details]] - (let [on-success (fn [derived-address-details] - (rf/dispatch [:wallet/add-account account-details - (first derived-address-details)]))] - {:fx [[:dispatch [:wallet/create-derived-addresses account-details on-success]]]}))) - -(defn add-keypair-and-create-account - [_ [{:keys [sha3-pwd new-keypair]}]] - (let [lowercase-address (if (:address new-keypair) - (string/lower-case (:address new-keypair)) - (:address new-keypair))] - {:fx [[:json-rpc/call - [{:method "accounts_addKeypair" - :params [sha3-pwd new-keypair] - :on-success [:wallet/add-account-success lowercase-address] - :on-error #(log/info "failed to create keypair " %)}]]]})) - -(rf/reg-event-fx :wallet/add-keypair-and-create-account add-keypair-and-create-account) + :on-error #(log/info "failed to create account " % account-config)}]]]}))) (defn get-keypairs [_] diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs index c002f25a5b..fefcb080e4 100644 --- a/src/status_im/subs/wallet/wallet.cljs +++ b/src/status_im/subs/wallet/wallet.cljs @@ -47,6 +47,11 @@ :<- [:wallet/ui] :-> :create-account) +(rf/reg-sub + :wallet/create-account-new-keypair + :<- [:wallet/create-account] + :-> :new-keypair) + (rf/reg-sub :wallet/network-filter :<- [:wallet/ui] @@ -165,6 +170,25 @@ :<- [:wallet/create-account] :-> :selected-keypair-uid) +(rf/reg-sub + :wallet/selected-keypair + :<- [:wallet/keypairs] + :<- [:wallet/selected-keypair-uid] + (fn [[keypairs selected-keypair-uid]] + (some #(when (= (:key-uid %) selected-keypair-uid) + %) + keypairs))) + +(rf/reg-sub + :wallet/selected-primary-keypair? + :<- [:wallet/keypairs] + :<- [:wallet/selected-keypair-uid] + (fn [[keypairs selected-keypair-uid]] + (let [primary-keypair-uid (->> keypairs + (some #(when (= (:type %) "profile") %)) + (:key-uid))] + (= selected-keypair-uid primary-keypair-uid)))) + (rf/reg-sub :wallet/selected-networks->chain-ids :<- [:wallet/selected-networks]