Wallet: create account using recovery phrase (#19702)

Wallet: create account using recovery phrase (#19702)
This commit is contained in:
Omar Basem 2024-04-26 11:34:55 +04:00 committed by GitHub
parent 1e55827c4b
commit 8aa586fe6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 94 additions and 57 deletions

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.onboarding.enter-seed-phrase.style
(ns status-im.common.enter-seed-phrase.style
(:require
[react-native.safe-area :as safe-area]))

View File

@ -1,4 +1,4 @@
(ns status-im.contexts.onboarding.enter-seed-phrase.view
(ns status-im.common.enter-seed-phrase.view
(:require
[clojure.string :as string]
[legacy.status-im.ethereum.mnemonic :as mnemonic]
@ -7,8 +7,8 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[reagent.core :as reagent]
[status-im.common.enter-seed-phrase.style :as style]
[status-im.constants :as constants]
[status-im.contexts.onboarding.enter-seed-phrase.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
@ -86,7 +86,7 @@
(take 7)))
(defn screen
[]
[recovering-keypair?]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
@ -103,9 +103,14 @@
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-seed-phrase)
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase]))]
(if recovering-keypair?
(rf/dispatch [:wallet/seed-phrase-entered
(security/mask-data
@seed-phrase)
set-invalid-seed-phrase])
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase])))]
(let [words-coll (mnemonic/passphrase->words @seed-phrase)
last-word (peek words-coll)
pick-suggested-word (fn [pressed-word]
@ -147,7 +152,7 @@
[rn/view {:style style/keyboard-container}
[quo/predictive-keyboard
{:type suggestions-state
:blur? true
:blur? (not recovering-keypair?)
:text suggestions-text
:words (keyboard-suggestions last-word)
:on-press pick-suggested-word}]])])
@ -155,14 +160,16 @@
(.remove keyboard-show-listener)
(.remove keyboard-hide-listener))))
(defn enter-seed-phrase
(defn view
[]
(let [{navigation-bar-top :top} (safe-area/get-insets)]
[rn/view {:style style/full-layout}
[rn/keyboard-avoiding-view {:style style/page-container}
[quo/page-nav
{:margin-top navigation-bar-top
:background :blur
:icon-name :i/arrow-left
:on-press #(rf/dispatch [:navigate-back])}]
[screen]]]))
(fn []
(let [{:keys [recovering-keypair?]} (rf/sub [:get-screen-params])]
[rn/view {:style style/full-layout}
[rn/keyboard-avoiding-view {:style style/page-container}
[quo/page-nav
{:margin-top navigation-bar-top
:background :blur
:icon-name (if recovering-keypair? :i/close :i/arrow-left)
:on-press #(rf/dispatch [:navigate-back])}]
[screen recovering-keypair?]]]))))

View File

@ -2,6 +2,7 @@
(:require [camel-snake-kebab.extras :as cske]
[status-im.contexts.wallet.data-store :as data-store]
[utils.re-frame :as rf]
[utils.security.core :as security]
[utils.transforms :as transforms]))
(defn get-keypairs-success
@ -21,14 +22,32 @@
(rf/reg-event-fx :wallet/confirm-account-origin confirm-account-origin)
(defn store-secret-phrase
[{:keys [db]} [{:keys [secret-phrase random-phrase]}]]
(defn store-seed-phrase
[{:keys [db]} [{:keys [seed-phrase random-phrase]}]]
{:db (-> db
(assoc-in [:wallet :ui :create-account :secret-phrase] secret-phrase)
(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.check-your-backup]}]]]})
(rf/reg-event-fx :wallet/store-secret-phrase store-secret-phrase)
(rf/reg-event-fx :wallet/store-seed-phrase store-seed-phrase)
(defn seed-phrase-validated
[{:keys [db]} [seed-phrase]]
{:db (assoc-in db [:wallet :ui :create-account :seed-phrase] seed-phrase)
:fx [[:dispatch [:navigate-to :screen/wallet.keypair-name]]]})
(rf/reg-event-fx :wallet/seed-phrase-validated seed-phrase-validated)
(defn seed-phrase-entered
[_ [seed-phrase on-error]]
{:fx [[:multiaccount/validate-mnemonic
[seed-phrase
(fn [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]}]]
@ -39,10 +58,10 @@
(defn new-keypair-continue
[{:keys [db]} [{:keys [keypair-name]}]]
(let [secret-phrase (get-in db [:wallet :ui :create-account :secret-phrase])]
(let [seed-phrase (get-in db [:wallet :ui :create-account :seed-phrase])]
{:fx [[:effects.wallet/create-account-from-mnemonic
{:secret-phrase secret-phrase
:keypair-name keypair-name}]]}))
{:seed-phrase (security/safe-unmask-data seed-phrase)
:keypair-name keypair-name}]]}))
(rf/reg-event-fx :wallet/new-keypair-continue new-keypair-continue)

View File

@ -12,12 +12,12 @@
result-db (:db effects)]
(is (match? result-db expected-db))))
(deftest store-secret-phrase
(deftest store-seed-phrase
(let [db {}
props [{:secret-phrase "test-secret" :random-phrase "random-test"}]
expected-db {:wallet {:ui {:create-account {:secret-phrase "test-secret"
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-secret-phrase {:db db} props)
effects (events/store-seed-phrase {:db db} props)
result-db (:db effects)]
(is (match? result-db expected-db))))
@ -30,10 +30,10 @@
(is (match? result-db expected-db))))
(deftest new-keypair-continue
(let [db {:wallet {:ui {:create-account {:secret-phrase "test-secret"}}}}
(let [db {:wallet {:ui {:create-account {:seed-phrase "test-secret"}}}}
props [{:keypair-name "test-keypair"}]
expected-effects [[:effects.wallet/create-account-from-mnemonic
{:secret-phrase "test-secret" :keypair-name "test-keypair"}]]
{:seed-phrase "test-secret" :keypair-name "test-keypair"}]]
effects (events/new-keypair-continue {:db db} props)]
(is (match? effects {:fx expected-effects}))))

View File

@ -10,7 +10,8 @@
[status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.style :as
style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn- word-item
[item index _ increment]
@ -54,14 +55,14 @@
:3 false})
revealed? (reagent/atom false)
customization-color (rf/sub [:profile/customization-color])
secret-phrase (reagent/atom [])
seed-phrase (reagent/atom [])
random-phrase (reagent/atom [])]
(fn []
(let [theme (quo.theme/use-theme)]
(rn/use-mount
(fn []
(native-module/get-random-mnemonic #(reset! secret-phrase (string/split % #"\s")))
(native-module/get-random-mnemonic #(reset! seed-phrase (string/split % #"\s")))
(native-module/get-random-mnemonic #(reset! random-phrase (string/split % #"\s")))))
[rn/view {:style {:flex 1}}
[quo/page-nav
@ -74,14 +75,14 @@
:description-text (i18n/label :t/backup-recovery-phrase-description)
:container-style {:padding-bottom 8}}]
[rn/view {:style (style/seed-phrase-container theme)}
(when (pos? (count @secret-phrase))
(when (pos? (count @seed-phrase))
[:<>
[words-column
{:words @secret-phrase
{:words @seed-phrase
:first-half? true}]
[rn/view {:style (style/separator theme)}]
[words-column
{:words @secret-phrase
{:words @seed-phrase
:first-half? false}]])
(when-not @revealed?
[rn/view {:style style/blur-container}
@ -107,8 +108,9 @@
: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-secret-phrase
{:secret-phrase @secret-phrase
:on-press #(rf/dispatch [:wallet/store-seed-phrase
{:seed-phrase (security/mask-data
@seed-phrase)
:random-phrase @random-phrase}])}}]
[quo/text
{:size :paragraph-2

View File

@ -5,7 +5,8 @@
[reagent.core :as reagent]
[status-im.contexts.wallet.add-account.create-account.new-keypair.check-your-backup.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[utils.security.core :as security]))
(def secret-words-count 12)
@ -60,15 +61,16 @@
(defn view
[]
(let [random-indices (random-selection)
quiz-index (reagent/atom 0)
incorrect-count (reagent/atom 0)
show-error? (reagent/atom false)
{:keys [secret-phrase random-phrase]} (rf/sub [:wallet/create-account])]
(let [random-indices (random-selection)
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])
unmasked-seed-phrase (security/safe-unmask-data seed-phrase)]
(fn []
(let [current-word-index (get random-indices
(min @quiz-index (dec questions-count)))
current-word (get secret-phrase current-word-index)
current-word (get unmasked-seed-phrase current-word-index)
[options-row-0 options-row-1] (random-words-with-string random-phrase current-word)
on-button-press (fn [word]
(if (= word current-word)
@ -113,7 +115,7 @@
:else
:disabled)
:word (get secret-phrase num)
:word (get unmasked-seed-phrase num)
:number (inc num)
:on-press #(when (= @quiz-index index)
(reset! show-error? false))}])

View File

@ -47,8 +47,8 @@
{:actions :one-action
:button-one-label (i18n/label :t/continue)
:button-one-props {:disabled? (or (pos? error)
(<= (count keypair-name)
keypair-name-min-length))
(< (count keypair-name)
keypair-name-min-length))
:customization-color customization-color
:on-press on-continue}
:container-style style/bottom-action}]}

View File

@ -20,7 +20,9 @@
{:icon :i/seed
:accessibility-label :import-using-phrase
:label (i18n/label :t/import-using-phrase)
:add-divider? true}
:add-divider? true
:on-press #(rf/dispatch [:navigate-to :screen/wallet.enter-seed-phrase
{:recovering-keypair? true}])}
{:icon :i/key
:accessibility-label :import-private-key
:label (i18n/label :t/import-private-key)

View File

@ -6,9 +6,11 @@
(rf/reg-fx
:effects.wallet/create-account-from-mnemonic
(fn [{:keys [secret-phrase keypair-name]}]
(fn [{:keys [seed-phrase keypair-name]}]
(native-module/create-account-from-mnemonic
{:MnemonicPhrase (string/join " " secret-phrase)}
{: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)}])))))

View File

@ -3,6 +3,7 @@
[legacy.status-im.ui.screens.screens :as old-screens]
[quo.foundations.colors :as colors]
[status-im.common.emoji-picker.view :as emoji-picker]
[status-im.common.enter-seed-phrase.view :as enter-seed-phrase]
[status-im.common.lightbox.view :as lightbox]
[status-im.config :as config]
[status-im.contexts.chat.group-create.view :as group-create]
@ -29,7 +30,6 @@
[status-im.contexts.onboarding.create-profile.view :as create-profile]
[status-im.contexts.onboarding.enable-biometrics.view :as enable-biometrics]
[status-im.contexts.onboarding.enable-notifications.view :as enable-notifications]
[status-im.contexts.onboarding.enter-seed-phrase.view :as enter-seed-phrase]
[status-im.contexts.onboarding.generating-keys.view :as generating-keys]
[status-im.contexts.onboarding.identifiers.view :as identifiers]
[status-im.contexts.onboarding.intro.view :as intro]
@ -304,7 +304,7 @@
:layout options/onboarding-transparent-layout
:animations transitions/push-animations-for-transparent-background
:popGesture false}
:component enter-seed-phrase/enter-seed-phrase}
:component enter-seed-phrase/view}
{:name :screen/onboarding.enable-notifications
:options {:theme :dark
@ -429,6 +429,9 @@
:options {:insets {:top? true :bottom? true}}
:component wallet-keypair-name/view}
{:name :screen/wallet.enter-seed-phrase
:component enter-seed-phrase/view}
{:name :screen/wallet.share-address
:options options/transparent-screen-options
:component wallet-share-address/view}

View File

@ -2546,8 +2546,8 @@
"derivation-path-desc": "Derivation paths are the routes your Status Wallet uses to generate addresses from your private key.",
"select-networks": "Select networks",
"generating-keypair": "Generating keypair...",
"keypair-name": "Keypair name",
"keypair-name-description": "Name keypair for your own personal reference",
"keypair-name": "Key pair name",
"keypair-name-description": "Name key pair for your own personal reference",
"keypair-name-input-placeholder": "Collectibles account, Old vault....",
"goerli-testnet-toggle-confirmation": "Are you sure you want to toggle Goerli? This will log you out and you will have to login again.",
"bridged-to": "Bridged to {{network}}",
@ -2582,7 +2582,6 @@
"one-user-was-invited": "1 user was invited",
"n-users-were-invited": "{{count}} users were invited",
"invite-friend-to-status": "Invite friends to Status",
"send-community-link": "Send community link",
"enter-private-key": "Enter the private key of an address",
"enter-private-key-placeholder": "Enter your private key",
"import-private-key-info": "New addresses cannot be derived from an account imported from a private key. Import using a seed phrase if you wish to derive addresses.",
@ -2590,5 +2589,6 @@
"private-key-public-address": "Public address of private key",
"this-account-has-no-activity": "This account has no activity",
"this-address-has-activity": "This address has activity",
"scanning-for-activity": "Scanning for activity..."
"scanning-for-activity": "Scanning for activity...",
"send-community-link": "Send community link"
}