[#7216] import account to keycard
Signed-off-by: Dmitry Novotochinov <dmitry.novot@gmail.com>
This commit is contained in:
parent
4c2054afd4
commit
908e80eb0b
|
@ -53,7 +53,7 @@
|
||||||
"react-native-safe-area-view": "0.9.0",
|
"react-native-safe-area-view": "0.9.0",
|
||||||
"react-native-securerandom": "git+https://github.com/status-im/react-native-securerandom.git#0.1.1-2",
|
"react-native-securerandom": "git+https://github.com/status-im/react-native-securerandom.git#0.1.1-2",
|
||||||
"react-native-splash-screen": "3.1.1",
|
"react-native-splash-screen": "3.1.1",
|
||||||
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.3.7",
|
"react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.3.8",
|
||||||
"react-native-svg": "6.5.2",
|
"react-native-svg": "6.5.2",
|
||||||
"react-native-tcp": "git+https://github.com/status-im/react-native-tcp.git#v3.3.0-1-status",
|
"react-native-tcp": "git+https://github.com/status-im/react-native-tcp.git#v3.3.0-1-status",
|
||||||
"react-native-udp": "git+https://github.com/status-im/react-native-udp.git#2.3.1-1",
|
"react-native-udp": "git+https://github.com/status-im/react-native-udp.git#2.3.1-1",
|
||||||
|
|
|
@ -5845,9 +5845,9 @@ react-native-splash-screen@3.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.1.1.tgz#1a4e46c9fdce53ff52af2a2cb4181788c4e30b30"
|
resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.1.1.tgz#1a4e46c9fdce53ff52af2a2cb4181788c4e30b30"
|
||||||
integrity sha512-PU2YocOSGbLjL9Vgcq/cwMNuHHKNjjuPpa1IPMuWo+6EB/fSZ5VOmxSa7+eucQe3631s3NhGuk3eHKahU03a4Q==
|
integrity sha512-PU2YocOSGbLjL9Vgcq/cwMNuHHKNjjuPpa1IPMuWo+6EB/fSZ5VOmxSa7+eucQe3631s3NhGuk3eHKahU03a4Q==
|
||||||
|
|
||||||
"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.3.7":
|
"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.3.8":
|
||||||
version "2.3.7"
|
version "2.3.8"
|
||||||
resolved "git+https://github.com/status-im/react-native-status-keycard.git#9e6df66bfd9288584031cff5455445230059720b"
|
resolved "git+https://github.com/status-im/react-native-status-keycard.git#e0448b0960a5b4041d2a04dda55bb39e2aa6b191"
|
||||||
|
|
||||||
react-native-svg@6.5.2:
|
react-native-svg@6.5.2:
|
||||||
version "6.5.2"
|
version "6.5.2"
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
[status-im.utils.types :as types]
|
[status-im.utils.types :as types]
|
||||||
[status-im.utils.fx :as fx]
|
[status-im.utils.fx :as fx]
|
||||||
[status-im.node.core :as node]))
|
[status-im.node.core :as node]
|
||||||
|
[clojure.string :as str]))
|
||||||
|
|
||||||
(defn check-password-errors [password]
|
(defn check-password-errors [password]
|
||||||
(cond (string/blank? password) :required-field
|
(cond (string/blank? password) :required-field
|
||||||
|
@ -67,12 +68,25 @@
|
||||||
(fx/defn validate-recover-result
|
(fx/defn validate-recover-result
|
||||||
[{:keys [db] :as cofx} {:keys [error pubkey address]} password]
|
[{:keys [db] :as cofx} {:keys [error pubkey address]} password]
|
||||||
(if (empty? error)
|
(if (empty? error)
|
||||||
(let [account {:pubkey pubkey
|
(let [account-address (-> address
|
||||||
:address address
|
(str/lower-case)
|
||||||
:photo-path (identicon/identicon pubkey)
|
(str/replace-first "0x" ""))
|
||||||
:mnemonic ""}]
|
keycard-account? (boolean (get-in db [:accounts/accounts account-address :keycard-instance-uid]))]
|
||||||
(accounts.create/on-account-created
|
(if keycard-account?
|
||||||
cofx account password {:seed-backed-up? true}))
|
;; trying to recover account created with keycard
|
||||||
|
{:db (-> db
|
||||||
|
(update :accounts/recover assoc
|
||||||
|
:processing? false
|
||||||
|
:passphrase-error :recover-keycard-account-not-supported)
|
||||||
|
(update :accounts/recover dissoc
|
||||||
|
:passphrase-valid?))
|
||||||
|
:node/stop nil}
|
||||||
|
(let [account {:pubkey pubkey
|
||||||
|
:address address
|
||||||
|
:photo-path (identicon/identicon pubkey)
|
||||||
|
:mnemonic ""}]
|
||||||
|
(accounts.create/on-account-created
|
||||||
|
cofx account password {:seed-backed-up? true}))))
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(update :accounts/recover assoc
|
(update :accounts/recover assoc
|
||||||
:processing? false
|
:processing? false
|
||||||
|
|
|
@ -227,7 +227,7 @@
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:accounts.recover.ui/recover-account-button-pressed
|
:accounts.recover.ui/recover-account-button-pressed
|
||||||
(fn [cofx _]
|
(fn [cofx _]
|
||||||
(accounts.recover/navigate-to-recover-account-screen cofx)))
|
(hardwallet/navigate-to-recover-method cofx)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:accounts.recover.ui/passphrase-input-changed
|
:accounts.recover.ui/passphrase-input-changed
|
||||||
|
@ -912,8 +912,8 @@
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet/get-application-info
|
:hardwallet/get-application-info
|
||||||
(fn [_ _]
|
(fn [cofx _]
|
||||||
{:hardwallet/get-application-info nil}))
|
(hardwallet/get-application-info cofx nil)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.callback/on-get-application-info-success
|
:hardwallet.callback/on-get-application-info-success
|
||||||
|
@ -1081,7 +1081,7 @@
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.ui/password-option-pressed
|
:hardwallet.ui/password-option-pressed
|
||||||
(fn [cofx _]
|
(fn [cofx _]
|
||||||
(accounts.create/navigate-to-create-account-screen cofx)))
|
(hardwallet/password-option-pressed cofx)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.ui/go-to-settings-button-pressed
|
:hardwallet.ui/go-to-settings-button-pressed
|
||||||
|
@ -1116,7 +1116,7 @@
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.ui/recovery-phrase-next-button-pressed
|
:hardwallet.ui/recovery-phrase-next-button-pressed
|
||||||
(fn [cofx _]
|
(fn [cofx _]
|
||||||
(hardwallet/recovery-phrase-start-confirmation cofx)))
|
(hardwallet/recovery-phrase-next-button-pressed cofx)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.ui/recovery-phrase-confirm-word-next-button-pressed
|
:hardwallet.ui/recovery-phrase-confirm-word-next-button-pressed
|
||||||
|
@ -1201,7 +1201,7 @@
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet.ui/card-ready-next-button-pressed
|
:hardwallet.ui/card-ready-next-button-pressed
|
||||||
(fn [cofx _]
|
(fn [cofx _]
|
||||||
(hardwallet/load-generating-mnemonic-screen cofx)))
|
(hardwallet/card-ready-next-button-pressed cofx)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:hardwallet/generate-mnemonic
|
:hardwallet/generate-mnemonic
|
||||||
|
|
|
@ -12,16 +12,18 @@
|
||||||
[status-im.utils.datetime :as utils.datetime]
|
[status-im.utils.datetime :as utils.datetime]
|
||||||
[status-im.data-store.accounts :as accounts-store]
|
[status-im.data-store.accounts :as accounts-store]
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[status-im.accounts.login.core :as accounts.login]))
|
[status-im.accounts.login.core :as accounts.login]
|
||||||
|
[status-im.accounts.recover.core :as accounts.recover]))
|
||||||
|
|
||||||
(def default-pin "000000")
|
(def default-pin "000000")
|
||||||
|
|
||||||
(defn- find-account-by-keycard-instance-uid
|
(defn- find-account-by-keycard-instance-uid
|
||||||
[db keycard-instance-uid]
|
[db keycard-instance-uid]
|
||||||
(->> (:accounts/accounts db)
|
(when keycard-instance-uid
|
||||||
vals
|
(->> (:accounts/accounts db)
|
||||||
(filter #(= keycard-instance-uid (:keycard-instance-uid %)))
|
vals
|
||||||
first))
|
(filter #(= keycard-instance-uid (:keycard-instance-uid %)))
|
||||||
|
first)))
|
||||||
|
|
||||||
(defn get-pairing
|
(defn get-pairing
|
||||||
([db]
|
([db]
|
||||||
|
@ -67,7 +69,8 @@
|
||||||
(fx/defn show-keycard-has-account-alert
|
(fx/defn show-keycard-has-account-alert
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
{:utils/show-confirmation {:title nil
|
{:db (assoc-in db [:hardwallet :setup-step] nil)
|
||||||
|
:utils/show-confirmation {:title nil
|
||||||
:content (i18n/label :t/keycard-has-account-on-it)
|
:content (i18n/label :t/keycard-has-account-on-it)
|
||||||
:cancel-button-text ""
|
:cancel-button-text ""
|
||||||
:confirm-button-text :t/okay}}
|
:confirm-button-text :t/okay}}
|
||||||
|
@ -117,14 +120,16 @@
|
||||||
(fx/defn check-card-state
|
(fx/defn check-card-state
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(let [app-info (get-in db [:hardwallet :application-info])
|
(let [app-info (get-in db [:hardwallet :application-info])
|
||||||
card-state (get-card-state app-info)]
|
card-state (get-card-state app-info)
|
||||||
|
setup-running? (boolean (get-in db [:hardwallet :setup-step]))]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
{:db (assoc-in db [:hardwallet :card-state] card-state)}
|
{:db (assoc-in db [:hardwallet :card-state] card-state)}
|
||||||
(when (= card-state :blank)
|
(when setup-running?
|
||||||
(show-no-keycard-applet-alert))
|
(when (= card-state :blank)
|
||||||
(if (= card-state :account)
|
(show-no-keycard-applet-alert))
|
||||||
(show-keycard-has-account-alert)
|
(if (= card-state :account)
|
||||||
(set-setup-step card-state)))))
|
(show-keycard-has-account-alert)
|
||||||
|
(set-setup-step card-state))))))
|
||||||
|
|
||||||
(fx/defn navigate-to-keycard-settings
|
(fx/defn navigate-to-keycard-settings
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
|
@ -148,11 +153,27 @@
|
||||||
(unauthorized-operation cofx))))
|
(unauthorized-operation cofx))))
|
||||||
|
|
||||||
(fx/defn navigate-to-authentication-method
|
(fx/defn navigate-to-authentication-method
|
||||||
[cofx]
|
[{:keys [db] :as cofx}]
|
||||||
(if (hardwallet-supported? cofx)
|
(if (hardwallet-supported? cofx)
|
||||||
(navigation/navigate-to-cofx cofx :hardwallet-authentication-method nil)
|
(fx/merge cofx
|
||||||
|
{:db (assoc-in db [:hardwallet :flow] :create)}
|
||||||
|
(navigation/navigate-to-cofx :hardwallet-authentication-method nil))
|
||||||
(accounts.create/navigate-to-create-account-screen cofx)))
|
(accounts.create/navigate-to-create-account-screen cofx)))
|
||||||
|
|
||||||
|
(fx/defn navigate-to-recover-method
|
||||||
|
[{:keys [db] :as cofx}]
|
||||||
|
(if (hardwallet-supported? cofx)
|
||||||
|
(fx/merge cofx
|
||||||
|
{:db (assoc-in db [:hardwallet :flow] :import)}
|
||||||
|
(navigation/navigate-to-cofx :hardwallet-authentication-method nil))
|
||||||
|
(accounts.recover/navigate-to-recover-account-screen cofx)))
|
||||||
|
|
||||||
|
(fx/defn password-option-pressed
|
||||||
|
[{:keys [db] :as cofx}]
|
||||||
|
(if (= (get-in db [:hardwallet :flow]) :create)
|
||||||
|
(accounts.create/navigate-to-create-account-screen cofx)
|
||||||
|
(accounts.recover/navigate-to-recover-account-screen cofx)))
|
||||||
|
|
||||||
(defn settings-screen-did-load
|
(defn settings-screen-did-load
|
||||||
[{:keys [db]}]
|
[{:keys [db]}]
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
|
@ -175,6 +196,12 @@
|
||||||
{:db (assoc-in db [:hardwallet :card-read-in-progress?] false)})
|
{:db (assoc-in db [:hardwallet :card-read-in-progress?] false)})
|
||||||
|
|
||||||
(defn accounts-screen-did-load
|
(defn accounts-screen-did-load
|
||||||
|
[{:keys [db]}]
|
||||||
|
{:db (-> db
|
||||||
|
(assoc-in [:hardwallet :setup-step] nil)
|
||||||
|
(dissoc :accounts/login))})
|
||||||
|
|
||||||
|
(defn authentication-method-screen-did-load
|
||||||
[{:keys [db]}]
|
[{:keys [db]}]
|
||||||
{:db (assoc-in db [:hardwallet :setup-step] nil)})
|
{:db (assoc-in db [:hardwallet :setup-step] nil)})
|
||||||
|
|
||||||
|
@ -182,6 +209,58 @@
|
||||||
[{:keys [db]} listeners]
|
[{:keys [db]} listeners]
|
||||||
{:db (update-in db [:hardwallet :listeners] merge listeners)})
|
{:db (update-in db [:hardwallet :listeners] merge listeners)})
|
||||||
|
|
||||||
|
(fx/defn get-keys-from-keycard
|
||||||
|
[{:keys [db]}]
|
||||||
|
(let [account-address (get-in db [:accounts/login :address])
|
||||||
|
pairing (get-in db [:accounts/accounts account-address :keycard-pairing])
|
||||||
|
pin (string/join (get-in db [:hardwallet :pin :login]))]
|
||||||
|
(when (and pairing
|
||||||
|
(not (empty? pin)))
|
||||||
|
{:db (-> db
|
||||||
|
(assoc-in [:hardwallet :pin :status] :verifying))
|
||||||
|
:hardwallet/get-keys {:pairing pairing
|
||||||
|
:pin pin}})))
|
||||||
|
|
||||||
|
(fx/defn login-with-keycard
|
||||||
|
[{:keys [db] :as cofx} auto-login?]
|
||||||
|
(let [account-login-address (get-in db [:accounts/login :address])
|
||||||
|
account-was-manually-selected? account-login-address
|
||||||
|
account-instance-uid (get-in db [:accounts/accounts account-login-address :keycard-instance-uid])
|
||||||
|
keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid])
|
||||||
|
account (find-account-by-keycard-instance-uid db keycard-instance-uid)
|
||||||
|
account-mismatch? (if account-was-manually-selected?
|
||||||
|
(not= account-instance-uid keycard-instance-uid)
|
||||||
|
(nil? account))
|
||||||
|
pairing (:keycard-pairing account)]
|
||||||
|
|
||||||
|
(cond
|
||||||
|
(empty? keycard-instance-uid)
|
||||||
|
(fx/merge cofx
|
||||||
|
{:utils/show-popup {:title (i18n/label :t/no-account-on-card)
|
||||||
|
:content (i18n/label :t/no-account-on-card-text)}}
|
||||||
|
(navigation/navigate-to-cofx :accounts nil))
|
||||||
|
|
||||||
|
account-mismatch?
|
||||||
|
(fx/merge cofx
|
||||||
|
{:db (dissoc db :accounts/login)
|
||||||
|
:utils/show-popup {:title (i18n/label (if auto-login? :t/account-not-listed :t/wrong-card))
|
||||||
|
:content (i18n/label (if auto-login? :t/account-not-listed-text :t/wrong-card-text))}}
|
||||||
|
(navigation/navigate-to-cofx :accounts nil))
|
||||||
|
|
||||||
|
(empty? pairing)
|
||||||
|
{:utils/show-popup {:title (i18n/label :t/error)
|
||||||
|
:content (i18n/label :t/no-pairing-on-device)}}
|
||||||
|
|
||||||
|
auto-login?
|
||||||
|
(fx/merge cofx
|
||||||
|
{:db (-> db
|
||||||
|
(assoc :accounts/login (select-keys account [:address :name :photo-path]))
|
||||||
|
(assoc-in [:hardwallet :pin :enter-step] :login))}
|
||||||
|
(navigation/navigate-to-cofx :enter-pin nil))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(get-keys-from-keycard cofx))))
|
||||||
|
|
||||||
(fx/defn clear-on-card-read
|
(fx/defn clear-on-card-read
|
||||||
[{:keys [db]}]
|
[{:keys [db]}]
|
||||||
{:db (assoc-in db [:hardwallet :on-card-read] nil)})
|
{:db (assoc-in db [:hardwallet :on-card-read] nil)})
|
||||||
|
@ -195,6 +274,11 @@
|
||||||
(let [info' (js->clj info :keywordize-keys true)
|
(let [info' (js->clj info :keywordize-keys true)
|
||||||
{:keys [pin-retry-counter puk-retry-counter]} info'
|
{:keys [pin-retry-counter puk-retry-counter]} info'
|
||||||
connect-screen? (= (:view-id db) :hardwallet-connect)
|
connect-screen? (= (:view-id db) :hardwallet-connect)
|
||||||
|
card-state (get-in db [:hardwallet :card-state])
|
||||||
|
instance-uid (get-in db [:hardwallet :application-info :instance-uid])
|
||||||
|
accounts-screen? (= :accounts (:view-id db))
|
||||||
|
auto-login? (and accounts-screen?
|
||||||
|
(not= on-success :hardwallet/auto-login))
|
||||||
enter-step (if (zero? pin-retry-counter)
|
enter-step (if (zero? pin-retry-counter)
|
||||||
:puk
|
:puk
|
||||||
(get-in db [:hardwallet :pin :enter-step]))]
|
(get-in db [:hardwallet :pin :enter-step]))]
|
||||||
|
@ -204,8 +288,13 @@
|
||||||
(assoc-in [:hardwallet :application-info] info')
|
(assoc-in [:hardwallet :application-info] info')
|
||||||
(assoc-in [:hardwallet :application-info :applet-installed?] true)
|
(assoc-in [:hardwallet :application-info :applet-installed?] true)
|
||||||
(assoc-in [:hardwallet :application-info-error] nil))}
|
(assoc-in [:hardwallet :application-info-error] nil))}
|
||||||
|
(when auto-login?
|
||||||
|
(login-with-keycard true))
|
||||||
(when-not connect-screen?
|
(when-not connect-screen?
|
||||||
(clear-on-card-read))
|
(clear-on-card-read))
|
||||||
|
(when (and (nil? card-state)
|
||||||
|
instance-uid)
|
||||||
|
(check-card-state))
|
||||||
(if (zero? puk-retry-counter)
|
(if (zero? puk-retry-counter)
|
||||||
(navigation/navigate-to-cofx :keycard-settings nil)
|
(navigation/navigate-to-cofx :keycard-settings nil)
|
||||||
(when on-success
|
(when on-success
|
||||||
|
@ -467,8 +556,13 @@
|
||||||
:error-label :t/puk-mismatch
|
:error-label :t/puk-mismatch
|
||||||
:enter-step :puk
|
:enter-step :puk
|
||||||
:puk []})}))
|
:puk []})}))
|
||||||
(fx/defn get-application-info [cofx pairing]
|
(fx/defn get-application-info
|
||||||
{:hardwallet/get-application-info {:pairing pairing}})
|
[{:keys [db]} pairing]
|
||||||
|
(let [instance-uid (get-in db [:hardwallet :application-info :instance-uid])
|
||||||
|
pairing' (or pairing
|
||||||
|
(when instance-uid
|
||||||
|
(get-pairing db instance-uid)))]
|
||||||
|
{:hardwallet/get-application-info {:pairing pairing'}}))
|
||||||
|
|
||||||
(fx/defn on-verify-pin-success
|
(fx/defn on-verify-pin-success
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
|
@ -592,18 +686,6 @@
|
||||||
:original []
|
:original []
|
||||||
:confirmation []}))
|
:confirmation []}))
|
||||||
|
|
||||||
(fx/defn get-keys-from-keycard
|
|
||||||
[{:keys [db]}]
|
|
||||||
(let [account-address (get-in db [:accounts/login :address])
|
|
||||||
pairing (get-in db [:accounts/accounts account-address :keycard-pairing])
|
|
||||||
pin (string/join (get-in db [:hardwallet :pin :login]))]
|
|
||||||
(when (and pairing
|
|
||||||
(not (empty? pin)))
|
|
||||||
{:db (-> db
|
|
||||||
(assoc-in [:hardwallet :pin :status] :verifying))
|
|
||||||
:hardwallet/get-keys {:pairing pairing
|
|
||||||
:pin pin}})))
|
|
||||||
|
|
||||||
(fx/defn wait-for-card-tap
|
(fx/defn wait-for-card-tap
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
|
@ -670,44 +752,6 @@
|
||||||
(let [{:keys [pairing]} (get-in cofx [:db :hardwallet :secrets])]
|
(let [{:keys [pairing]} (get-in cofx [:db :hardwallet :secrets])]
|
||||||
{:hardwallet/generate-mnemonic {:pairing pairing}}))
|
{:hardwallet/generate-mnemonic {:pairing pairing}}))
|
||||||
|
|
||||||
(defn login-with-keycard
|
|
||||||
[{:keys [db] :as cofx} auto-login?]
|
|
||||||
(let [account-login-address (get-in db [:accounts/login :address])
|
|
||||||
account-was-manually-selected? account-login-address
|
|
||||||
account-instance-uid (get-in db [:accounts/accounts account-login-address :keycard-instance-uid])
|
|
||||||
keycard-instance-uid (get-in db [:hardwallet :application-info :instance-uid])
|
|
||||||
account (find-account-by-keycard-instance-uid db keycard-instance-uid)
|
|
||||||
account-mismatch? (if account-was-manually-selected?
|
|
||||||
(not= account-instance-uid keycard-instance-uid)
|
|
||||||
(nil? account))
|
|
||||||
pairing (:keycard-pairing account)]
|
|
||||||
|
|
||||||
(cond
|
|
||||||
(empty? keycard-instance-uid)
|
|
||||||
(fx/merge cofx
|
|
||||||
{:utils/show-popup {:title (i18n/label :t/no-account-on-card)
|
|
||||||
:content (i18n/label :t/no-account-on-card-text)}}
|
|
||||||
(navigation/navigate-to-cofx :accounts nil))
|
|
||||||
|
|
||||||
account-mismatch?
|
|
||||||
(fx/merge cofx
|
|
||||||
{:db (dissoc db :accounts/login)
|
|
||||||
:utils/show-popup {:title (i18n/label (if auto-login? :t/account-not-listed :t/wrong-card))
|
|
||||||
:content (i18n/label (if auto-login? :t/account-not-listed-text :t/wrong-card-text))}}
|
|
||||||
(navigation/navigate-to-cofx :accounts nil))
|
|
||||||
|
|
||||||
(empty? pairing)
|
|
||||||
{:utils/show-popup {:title (i18n/label :t/error)
|
|
||||||
:content (i18n/label :t/no-pairing-on-device)}}
|
|
||||||
|
|
||||||
auto-login?
|
|
||||||
(fx/merge cofx
|
|
||||||
{:db (assoc db :accounts/login (select-keys account [:address :name :photo-path]))}
|
|
||||||
(navigation/navigate-to-cofx :enter-pin nil))
|
|
||||||
|
|
||||||
:else
|
|
||||||
(get-keys-from-keycard cofx))))
|
|
||||||
|
|
||||||
(fx/defn on-card-connected
|
(fx/defn on-card-connected
|
||||||
[{:keys [db] :as cofx} data]
|
[{:keys [db] :as cofx} data]
|
||||||
(log/debug "[hardwallet] card connected " data)
|
(log/debug "[hardwallet] card connected " data)
|
||||||
|
@ -716,14 +760,14 @@
|
||||||
setup-running? (boolean setup-step)
|
setup-running? (boolean setup-step)
|
||||||
pin-enter-step (get-in db [:hardwallet :pin :enter-step])
|
pin-enter-step (get-in db [:hardwallet :pin :enter-step])
|
||||||
login? (= :login pin-enter-step)
|
login? (= :login pin-enter-step)
|
||||||
accounts-screen? (= :accounts (:view-id db))
|
|
||||||
auto-login? accounts-screen?
|
|
||||||
instance-uid (get-in db [:hardwallet :application-info :instance-uid])
|
instance-uid (get-in db [:hardwallet :application-info :instance-uid])
|
||||||
|
accounts-screen? (= :accounts (:view-id db))
|
||||||
|
auto-login? (and accounts-screen? instance-uid)
|
||||||
should-read-instance-uid? (nil? instance-uid)
|
should-read-instance-uid? (nil? instance-uid)
|
||||||
on-card-connected (get-in db [:hardwallet :on-card-connected])
|
on-card-connected (get-in db [:hardwallet :on-card-connected])
|
||||||
on-card-read (cond
|
on-card-read (cond
|
||||||
auto-login? :hardwallet/auto-login
|
|
||||||
should-read-instance-uid? :hardwallet/get-application-info
|
should-read-instance-uid? :hardwallet/get-application-info
|
||||||
|
auto-login? :hardwallet/auto-login
|
||||||
:else (get-in db [:hardwallet :on-card-read]))
|
:else (get-in db [:hardwallet :on-card-read]))
|
||||||
pairing (get-pairing db instance-uid)]
|
pairing (get-pairing db instance-uid)]
|
||||||
(fx/merge cofx
|
(fx/merge cofx
|
||||||
|
@ -762,7 +806,7 @@
|
||||||
(assoc-in [:hardwallet :pin :confirmation] []))})
|
(assoc-in [:hardwallet :pin :confirmation] []))})
|
||||||
|
|
||||||
(fx/defn start-installation
|
(fx/defn start-installation
|
||||||
[{:keys [db]}]
|
[{:keys [db] :as cofx}]
|
||||||
(let [card-state (get-in db [:hardwallet :card-state])
|
(let [card-state (get-in db [:hardwallet :card-state])
|
||||||
pin (vector->string (get-in db [:hardwallet :pin :original]))]
|
pin (vector->string (get-in db [:hardwallet :pin :original]))]
|
||||||
(case card-state
|
(case card-state
|
||||||
|
@ -778,15 +822,18 @@
|
||||||
|
|
||||||
(do
|
(do
|
||||||
(log/debug (str "Cannot start keycard installation from state: " card-state))
|
(log/debug (str "Cannot start keycard installation from state: " card-state))
|
||||||
{:utils/show-popup {:title (i18n/label :t/error)
|
(fx/merge cofx
|
||||||
:content (i18n/label :t/something-went-wrong)}}))))
|
{:utils/show-popup {:title (i18n/label :t/error)
|
||||||
|
:content (i18n/label :t/something-went-wrong)}}
|
||||||
|
(navigation/navigate-to-cofx :hardwallet-authentication-method nil))))))
|
||||||
|
|
||||||
(fx/defn on-install-applet-and-init-card-success
|
(fx/defn on-install-applet-and-init-card-success
|
||||||
[{:keys [db]} secrets]
|
[{:keys [db]} secrets]
|
||||||
(let [secrets' (js->clj secrets :keywordize-keys true)]
|
(let [secrets' (js->clj secrets :keywordize-keys true)]
|
||||||
{:db (-> db
|
{:hardwallet/get-application-info nil
|
||||||
(assoc-in [:hardwallet :setup-step] :secret-keys)
|
:db (-> db
|
||||||
(assoc-in [:hardwallet :secrets] secrets'))}))
|
(assoc-in [:hardwallet :setup-step] :secret-keys)
|
||||||
|
(assoc-in [:hardwallet :secrets] secrets'))}))
|
||||||
|
|
||||||
(def on-init-card-success on-install-applet-and-init-card-success)
|
(def on-init-card-success on-install-applet-and-init-card-success)
|
||||||
|
|
||||||
|
@ -814,7 +861,7 @@
|
||||||
(let [account' (assoc account :keycard-pairing pairing
|
(let [account' (assoc account :keycard-pairing pairing
|
||||||
:keycard-paired-on paired-on)]
|
:keycard-paired-on paired-on)]
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(assoc db :accounts/account account')
|
(assoc :accounts/account account')
|
||||||
(assoc-in [:accounts/accounts address] account'))
|
(assoc-in [:accounts/accounts address] account'))
|
||||||
:data-store/base-tx [(accounts-store/save-account-tx account')]}))
|
:data-store/base-tx [(accounts-store/save-account-tx account')]}))
|
||||||
|
|
||||||
|
@ -905,6 +952,21 @@
|
||||||
(show-recover-confirmation))
|
(show-recover-confirmation))
|
||||||
{:db (assoc-in db [:hardwallet :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))})))
|
{:db (assoc-in db [:hardwallet :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))})))
|
||||||
|
|
||||||
|
(fx/defn card-ready-next-button-pressed
|
||||||
|
[{:keys [db] :as cofx}]
|
||||||
|
(if (= (get-in db [:hardwallet :flow]) :create)
|
||||||
|
(load-generating-mnemonic-screen cofx)
|
||||||
|
{:db (assoc-in db [:hardwallet :setup-step] :recovery-phrase)}))
|
||||||
|
|
||||||
|
(fx/defn recovery-phrase-next-button-pressed
|
||||||
|
[{:keys [db] :as cofx}]
|
||||||
|
(if (= (get-in db [:hardwallet :flow]) :create)
|
||||||
|
(recovery-phrase-start-confirmation cofx)
|
||||||
|
(let [mnemonic (get-in db [:accounts/recover :passphrase])]
|
||||||
|
(fx/merge cofx
|
||||||
|
{:db (assoc-in db [:hardwallet :secrets :mnemonic] mnemonic)}
|
||||||
|
(load-loading-keys-screen)))))
|
||||||
|
|
||||||
(fx/defn generate-and-load-key
|
(fx/defn generate-and-load-key
|
||||||
[{:keys [db] :as cofx}]
|
[{:keys [db] :as cofx}]
|
||||||
(let [{:keys [mnemonic pairing]} (get-in db [:hardwallet :secrets])
|
(let [{:keys [mnemonic pairing]} (get-in db [:hardwallet :secrets])
|
||||||
|
|
|
@ -178,5 +178,6 @@
|
||||||
:reset-card (hardwallet/reset-card-screen-did-load %)
|
:reset-card (hardwallet/reset-card-screen-did-load %)
|
||||||
:enter-pin (hardwallet/enter-pin-screen-did-load %)
|
:enter-pin (hardwallet/enter-pin-screen-did-load %)
|
||||||
:hardwallet-connect (hardwallet/hardwallet-connect-screen-did-load %)
|
:hardwallet-connect (hardwallet/hardwallet-connect-screen-did-load %)
|
||||||
|
:hardwallet-authentication-method (hardwallet/authentication-method-screen-did-load %)
|
||||||
:accounts (hardwallet/accounts-screen-did-load %)
|
:accounts (hardwallet/accounts-screen-did-load %)
|
||||||
nil))))
|
nil))))
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
:number 4
|
:number 4
|
||||||
:next-step :recovery-phrase}
|
:next-step :recovery-phrase}
|
||||||
:card-ready {:label :t/pairing
|
:card-ready {:label :t/pairing
|
||||||
:number 5
|
:number 4
|
||||||
:next-step :recovery-phrase}
|
:next-step :recovery-phrase}
|
||||||
:generating-mnemonic {:label :t/recovery-phrase
|
:generating-mnemonic {:label :t/recovery-phrase
|
||||||
:number 5}
|
:number 5}
|
||||||
|
|
|
@ -6,9 +6,7 @@
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:keycard-paired-on
|
:keycard-paired-on
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(some-> (or
|
(some-> (get-in db [:account/account :keycard-paired-on])
|
||||||
(get-in db [:hardwallet :secrets :paired-on])
|
|
||||||
(get-in db [:account/account :keycard-paired-on]))
|
|
||||||
(utils.datetime/timestamp->year-month-day-date))))
|
(utils.datetime/timestamp->year-month-day-date))))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
|
@ -16,6 +14,11 @@
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(core/get-pairing db)))
|
(core/get-pairing db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:keycard-account-pairing
|
||||||
|
(fn [db]
|
||||||
|
(get-in db [:account/account :keycard-pairing])))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:hardwallet/pin-retry-counter
|
:hardwallet/pin-retry-counter
|
||||||
(fn [db]
|
(fn [db]
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
(defview keycard-settings []
|
(defview keycard-settings []
|
||||||
(letsubs [paired-on [:keycard-paired-on]
|
(letsubs [paired-on [:keycard-paired-on]
|
||||||
puk-retry-counter [:hardwallet/puk-retry-counter]
|
puk-retry-counter [:hardwallet/puk-retry-counter]
|
||||||
pairing [:keycard-pairing]]
|
pairing [:keycard-account-pairing]]
|
||||||
[react/view {:flex 1}
|
[react/view {:flex 1}
|
||||||
[status-bar/status-bar]
|
[status-bar/status-bar]
|
||||||
[toolbar/simple-toolbar
|
[toolbar/simple-toolbar
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(get-in db [:hardwallet :card-state])))
|
(get-in db [:hardwallet :card-state])))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:hardwallet-flow
|
||||||
|
(fn [db]
|
||||||
|
(get-in db [:hardwallet :flow])))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:hardwallet-pair-code
|
:hardwallet-pair-code
|
||||||
(fn [db]
|
(fn [db]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns status-im.ui.screens.hardwallet.setup.views
|
(ns status-im.ui.screens.hardwallet.setup.views
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
|
[goog.functions :refer [debounce]]
|
||||||
[status-im.react-native.js-dependencies :as js-dependencies]
|
[status-im.react-native.js-dependencies :as js-dependencies]
|
||||||
[status-im.ui.screens.profile.seed.views :as seed.views]
|
[status-im.ui.screens.profile.seed.views :as seed.views]
|
||||||
[status-im.ui.screens.hardwallet.components :as components]
|
[status-im.ui.screens.hardwallet.components :as components]
|
||||||
|
@ -16,7 +17,8 @@
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.utils.utils :as utils]
|
[status-im.utils.utils :as utils]
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.ui.screens.hardwallet.setup.styles :as styles]))
|
[status-im.ui.screens.hardwallet.setup.styles :as styles]
|
||||||
|
[status-im.utils.security :as security]))
|
||||||
|
|
||||||
(defonce event-emitter (.-DeviceEventEmitter js-dependencies/react-native))
|
(defonce event-emitter (.-DeviceEventEmitter js-dependencies/react-native))
|
||||||
|
|
||||||
|
@ -71,24 +73,27 @@
|
||||||
:uppercase? false
|
:uppercase? false
|
||||||
:forward? true}]]]]]))
|
:forward? true}]]]]]))
|
||||||
|
|
||||||
(defn card-ready []
|
(defview card-ready []
|
||||||
[react/view styles/card-ready-container
|
(letsubs [flow [:hardwallet-flow]]
|
||||||
[react/view styles/card-ready-inner-container
|
[react/view styles/card-ready-container
|
||||||
[react/view (assoc styles/center-container :margin-top 68)
|
[react/view styles/card-ready-inner-container
|
||||||
[react/text {:style styles/center-title-text}
|
[react/view (assoc styles/center-container :margin-top 68)
|
||||||
(i18n/label :t/card-is-paired)]
|
[react/text {:style styles/center-title-text}
|
||||||
[react/text {:style styles/estimated-time-text}
|
(i18n/label :t/card-is-paired)]
|
||||||
(i18n/label :t/next-step-generating-mnemonic)]]
|
[react/text {:style styles/estimated-time-text}
|
||||||
[react/view]
|
(if (= flow :create)
|
||||||
[react/view styles/next-button-container
|
(i18n/label :t/next-step-generating-mnemonic)
|
||||||
[react/view components.styles/flex]
|
(i18n/label :t/next-step-entering-mnemonic))]]
|
||||||
[react/view {:margin-right 20}
|
[react/view]
|
||||||
[components.common/bottom-button
|
[react/view styles/next-button-container
|
||||||
{:on-press #(re-frame/dispatch [:hardwallet.ui/card-ready-next-button-pressed])
|
[react/view components.styles/flex]
|
||||||
:uppercase? false
|
[react/view {:margin-right 20}
|
||||||
:forward? true}]]]]])
|
[components.common/bottom-button
|
||||||
|
{:on-press #(re-frame/dispatch [:hardwallet.ui/card-ready-next-button-pressed])
|
||||||
|
:uppercase? false
|
||||||
|
:forward? true}]]]]]))
|
||||||
|
|
||||||
(defview recovery-phrase []
|
(defview display-recovery-phrase []
|
||||||
(letsubs [mnemonic [:hardwallet-mnemonic]]
|
(letsubs [mnemonic [:hardwallet-mnemonic]]
|
||||||
(let [mnemonic-vec (vec (map-indexed vector (clojure.string/split mnemonic #" ")))]
|
(let [mnemonic-vec (vec (map-indexed vector (clojure.string/split mnemonic #" ")))]
|
||||||
[react/view styles/card-ready-container
|
[react/view styles/card-ready-container
|
||||||
|
@ -98,21 +103,22 @@
|
||||||
:number-of-lines 2
|
:number-of-lines 2
|
||||||
:font :bold}
|
:font :bold}
|
||||||
(i18n/label :t/your-recovery-phrase)]
|
(i18n/label :t/your-recovery-phrase)]
|
||||||
[react/view {:style {:margin-top 17
|
[react/view
|
||||||
:margin-bottom 16
|
[react/view {:style {:margin-top 17
|
||||||
:margin-horizontal 16
|
:margin-bottom 16
|
||||||
:flex-direction :row
|
:margin-horizontal 16
|
||||||
:border-radius 8
|
:flex-direction :row
|
||||||
:background-color colors/white
|
:border-radius 8
|
||||||
:border-width 1
|
:background-color colors/white
|
||||||
:border-color colors/gray-lighter}}
|
:border-width 1
|
||||||
[seed.views/six-words (subvec mnemonic-vec 0 6)]
|
:border-color colors/gray-lighter}}
|
||||||
[react/view {:style {:width 1
|
[seed.views/six-words (subvec mnemonic-vec 0 6)]
|
||||||
:background-color colors/gray-lighter}}]
|
[react/view {:style {:width 1
|
||||||
[seed.views/six-words (subvec mnemonic-vec 6 12)]]
|
:background-color colors/gray-lighter}}]
|
||||||
[react/view styles/recovery-phrase-description
|
[seed.views/six-words (subvec mnemonic-vec 6 12)]]
|
||||||
[react/text {:style styles/recovery-phrase-description-text}
|
[react/view styles/recovery-phrase-description
|
||||||
(i18n/label :t/your-recovery-phrase-description)]]]]
|
[react/text {:style styles/recovery-phrase-description-text}
|
||||||
|
(i18n/label :t/your-recovery-phrase-description)]]]]]
|
||||||
[react/view styles/next-button-container
|
[react/view styles/next-button-container
|
||||||
[react/view components.styles/flex]
|
[react/view components.styles/flex]
|
||||||
[react/view {:margin-right 20}
|
[react/view {:margin-right 20}
|
||||||
|
@ -161,6 +167,51 @@
|
||||||
:uppercase? false
|
:uppercase? false
|
||||||
:forward? true}]]])))
|
:forward? true}]]])))
|
||||||
|
|
||||||
|
(defview enter-recovery-phrase []
|
||||||
|
(letsubs [width [:dimensions/window-width]
|
||||||
|
recovered-account [:get-recover-account]]
|
||||||
|
(let [{:keys [passphrase passphrase-valid? passphrase-error passphrase-warning]} recovered-account
|
||||||
|
disabled? (not passphrase-valid?)
|
||||||
|
validate-passphrase (debounce
|
||||||
|
#(re-frame/dispatch [:accounts.recover.ui/passphrase-input-blured])
|
||||||
|
1000)]
|
||||||
|
[react/view styles/enter-pair-code-container
|
||||||
|
[react/view styles/enter-pair-code-title-container
|
||||||
|
[react/view
|
||||||
|
[react/text {:style styles/center-title-text
|
||||||
|
:number-of-lines 2}
|
||||||
|
(i18n/label :t/your-recovery-phrase)]
|
||||||
|
[react/view (styles/enter-pair-code-input-container width)
|
||||||
|
[text-input/text-input-with-label
|
||||||
|
{:style {:flex 1
|
||||||
|
:text-align-vertical :top}
|
||||||
|
:height 92
|
||||||
|
:accessibility-label :enter-12-words
|
||||||
|
:placeholder (i18n/label :t/enter-12-words)
|
||||||
|
:multiline true
|
||||||
|
:default-value passphrase
|
||||||
|
:auto-correct false
|
||||||
|
:on-change-text #(do
|
||||||
|
(re-frame/dispatch [:accounts.recover.ui/passphrase-input-changed (security/mask-data %)])
|
||||||
|
(validate-passphrase))
|
||||||
|
:error (cond passphrase-error (i18n/label passphrase-error)
|
||||||
|
passphrase-warning (i18n/label passphrase-warning))}]]]]
|
||||||
|
[react/view styles/next-button-container
|
||||||
|
[react/view components.styles/flex]
|
||||||
|
[react/view {:margin-right 20}
|
||||||
|
[components.common/bottom-button
|
||||||
|
{:on-press #(re-frame/dispatch [:hardwallet.ui/recovery-phrase-next-button-pressed])
|
||||||
|
:label (i18n/label :t/next)
|
||||||
|
:disabled? disabled?
|
||||||
|
:uppercase? false
|
||||||
|
:forward? true}]]]])))
|
||||||
|
|
||||||
|
(defview recovery-phrase []
|
||||||
|
(letsubs [flow [:hardwallet-flow]]
|
||||||
|
(if (= flow :create)
|
||||||
|
(display-recovery-phrase)
|
||||||
|
(enter-recovery-phrase))))
|
||||||
|
|
||||||
(defview enter-pair-code []
|
(defview enter-pair-code []
|
||||||
(letsubs [pair-code [:hardwallet-pair-code]
|
(letsubs [pair-code [:hardwallet-pair-code]
|
||||||
width [:dimensions/window-width]]
|
width [:dimensions/window-width]]
|
||||||
|
@ -174,9 +225,8 @@
|
||||||
(i18n/label :t/enter-pair-code-description)]]
|
(i18n/label :t/enter-pair-code-description)]]
|
||||||
[react/view (styles/enter-pair-code-input-container width)
|
[react/view (styles/enter-pair-code-input-container width)
|
||||||
[text-input/text-input-with-label
|
[text-input/text-input-with-label
|
||||||
{:on-change-text #(re-frame/dispatch [:hardwallet.ui/pair-code-input-changed %])
|
{:on-change-text #(re-frame/dispatch [:hardwallet.ui/pair-code-input-changed %])
|
||||||
:secure-text-entry true
|
:placeholder ""}]]]
|
||||||
:placeholder ""}]]]
|
|
||||||
[react/view styles/next-button-container
|
[react/view styles/next-button-container
|
||||||
[react/view components.styles/flex]
|
[react/view components.styles/flex]
|
||||||
[react/view {:margin-right 20}
|
[react/view {:margin-right 20}
|
||||||
|
|
|
@ -776,6 +776,7 @@
|
||||||
"currency-display-name-uah": "Ukraine Hryvnia",
|
"currency-display-name-uah": "Ukraine Hryvnia",
|
||||||
"web3-opt-in": "Browser privacy mode",
|
"web3-opt-in": "Browser privacy mode",
|
||||||
"recover-password-invalid": "This account already exists but passwords do not match",
|
"recover-password-invalid": "This account already exists but passwords do not match",
|
||||||
|
"recover-keycard-account-not-supported": "Recovering keycard account with password is not supported",
|
||||||
"members-active-none": "no members",
|
"members-active-none": "no members",
|
||||||
"members-none": "no members",
|
"members-none": "no members",
|
||||||
"default-dapps-exchanges": "Exchanges",
|
"default-dapps-exchanges": "Exchanges",
|
||||||
|
@ -830,6 +831,7 @@
|
||||||
"completing-card-setup": "Completing card setup",
|
"completing-card-setup": "Completing card setup",
|
||||||
"generating-mnemonic": "Generating mnemonic phrase",
|
"generating-mnemonic": "Generating mnemonic phrase",
|
||||||
"next-step-generating-mnemonic": "Next step is generating mnemonic phrase for your card",
|
"next-step-generating-mnemonic": "Next step is generating mnemonic phrase for your card",
|
||||||
|
"next-step-entering-mnemonic": "Next step is entering mnemonic phrase to import your account",
|
||||||
"this-will-take-few-seconds": "This will take a few seconds",
|
"this-will-take-few-seconds": "This will take a few seconds",
|
||||||
"finishing-card-setup": "Finishing card setup",
|
"finishing-card-setup": "Finishing card setup",
|
||||||
"finishing-card-setup-steps": "> Loading keys to the card\n> Generating account",
|
"finishing-card-setup-steps": "> Loading keys to the card\n> Generating account",
|
||||||
|
|
Loading…
Reference in New Issue