feat: enable biometrics for standard-auth (#17992)
This commit is contained in:
parent
6093d56c90
commit
55a99da2f5
|
@ -46,6 +46,7 @@
|
|||
[status-im.wallet.core :as wallet]
|
||||
status-im.wallet.custom-tokens.core
|
||||
[status-im2.common.biometric.events :as biometric]
|
||||
status-im2.common.standard-authentication.events
|
||||
[status-im2.common.theme.core :as theme]
|
||||
[status-im2.common.universal-links :as universal-links]
|
||||
[status-im2.constants :as constants]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
(ns status-im2.common.standard-authentication.core
|
||||
(:require
|
||||
status-im2.common.standard-authentication.standard-auth.button.view
|
||||
status-im2.common.standard-authentication.password-input.view
|
||||
status-im2.common.standard-authentication.standard-auth.slide-button.view))
|
||||
|
||||
(def button status-im2.common.standard-authentication.standard-auth.button.view/view)
|
||||
(def password-input status-im2.common.standard-authentication.password-input.view/view)
|
||||
(def slide-button status-im2.common.standard-authentication.standard-auth.slide-button.view/view)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
[{:keys [on-enter-password button-label button-icon-left]}]
|
||||
[{:keys [on-enter-password on-press-biometrics button-label button-icon-left]}]
|
||||
(let [{:keys [key-uid customization-color] :as profile} (rf/sub [:profile/profile-with-image])
|
||||
{:keys [error processing password]} (rf/sub [:profile/login])
|
||||
sign-in-enabled? (rf/sub [:sign-in-enabled?])]
|
||||
|
@ -32,20 +32,21 @@
|
|||
:customization-color customization-color
|
||||
:size 24}]]
|
||||
[password-input/view
|
||||
{:processing processing
|
||||
:error error
|
||||
:default-password password
|
||||
:sign-in-enabled? sign-in-enabled?}]
|
||||
[rn/view {:style style/enter-password-button}
|
||||
[quo/button
|
||||
{:size 40
|
||||
:type :primary
|
||||
:customization-color (or customization-color :primary)
|
||||
:accessibility-label :login-button
|
||||
:icon-left button-icon-left
|
||||
:disabled? (or (not sign-in-enabled?) processing)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:set-in [:profile/login :key-uid] key-uid])
|
||||
(rf/dispatch [:profile.login/verify-database-password password
|
||||
#(on-enter-password password)]))}
|
||||
button-label]]]]]))
|
||||
{:on-press-biometrics on-press-biometrics
|
||||
:processing processing
|
||||
:error error
|
||||
:default-password password
|
||||
:sign-in-enabled? sign-in-enabled?}]
|
||||
[quo/button
|
||||
{:size 40
|
||||
:container-style style/enter-password-button
|
||||
:type :primary
|
||||
:customization-color (or customization-color :primary)
|
||||
:accessibility-label :login-button
|
||||
:icon-left button-icon-left
|
||||
:disabled? (or (not sign-in-enabled?) processing)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:set-in [:profile/login :key-uid] key-uid])
|
||||
(rf/dispatch [:profile.login/verify-database-password password
|
||||
#(on-enter-password password)]))}
|
||||
button-label]]]]))
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
(ns status-im2.common.standard-authentication.events
|
||||
(:require
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-event-fx :standard-auth/on-biometric-success
|
||||
(fn [{:keys [db]} [callback]]
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])]
|
||||
{:fx [[:keychain/get-user-password [key-uid callback]]]})))
|
|
@ -4,3 +4,7 @@
|
|||
{:margin-top 8
|
||||
:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def auth-button
|
||||
{:align-self :flex-end
|
||||
:margin-left 8})
|
||||
|
|
|
@ -27,21 +27,30 @@
|
|||
(rf/dispatch [:set-in [:profile/login :error] ""]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [default-password theme shell?]}]
|
||||
[{:keys [default-password theme shell? on-press-biometrics]}]
|
||||
(let [{:keys [error processing]} (rf/sub [:profile/login])
|
||||
error-message (get-error-message error)
|
||||
error? (boolean (seq error-message))]
|
||||
[:<>
|
||||
[quo/input
|
||||
{:type :password
|
||||
:blur? true
|
||||
:disabled? processing
|
||||
:placeholder (i18n/label :t/type-your-password)
|
||||
:auto-focus true
|
||||
:error? error?
|
||||
:label (i18n/label :t/profile-password)
|
||||
:on-change-text on-change-password
|
||||
:default-value (security/safe-unmask-data default-password)}]
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[quo/input
|
||||
{:container-style {:flex 1}
|
||||
:type :password
|
||||
:blur? true
|
||||
:disabled? processing
|
||||
:placeholder (i18n/label :t/type-your-password)
|
||||
:auto-focus true
|
||||
:error? error?
|
||||
:label (i18n/label :t/profile-password)
|
||||
:on-change-text on-change-password
|
||||
:default-value (security/safe-unmask-data default-password)}]
|
||||
(when on-press-biometrics
|
||||
[quo/button
|
||||
{:container-style style/auth-button
|
||||
:on-press on-press-biometrics
|
||||
:icon-only? true
|
||||
:type :outline}
|
||||
:i/face-id])]
|
||||
(when error?
|
||||
[rn/view {:style style/error-message}
|
||||
[quo/info-message
|
||||
|
@ -60,10 +69,7 @@
|
|||
:shell? shell?}]))}
|
||||
[rn/text
|
||||
{:style {:text-decoration-line :underline
|
||||
:color (colors/theme-colors
|
||||
(colors/custom-color :danger 50)
|
||||
(colors/custom-color :danger 60)
|
||||
theme)}
|
||||
:color (colors/resolve-color :danger theme)}
|
||||
:size :paragraph-2
|
||||
:suppress-highlighting true}
|
||||
(i18n/label :t/forgot-password)]]])]))
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
(ns status-im2.common.standard-authentication.standard-auth.authorize
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[react-native.touch-id :as biometric]
|
||||
[status-im2.common.standard-authentication.enter-password.view :as enter-password]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn reset-password
|
||||
[]
|
||||
|
@ -12,33 +14,49 @@
|
|||
(rf/dispatch [:set-in [:profile/login :error] ""]))
|
||||
|
||||
(defn authorize
|
||||
[{:keys [on-enter-password biometric-auth? on-auth-success on-auth-fail on-close
|
||||
[{:keys [biometric-auth? on-auth-success on-auth-fail on-close
|
||||
auth-button-label theme blur? auth-button-icon-left]}]
|
||||
(biometric/get-supported-type
|
||||
(fn [biometric-type]
|
||||
(if (and biometric-auth? biometric-type)
|
||||
(biometric/authenticate
|
||||
{:reason (i18n/label :t/biometric-auth-confirm-message)
|
||||
:on-success (fn [response]
|
||||
(when on-auth-success (on-auth-success response))
|
||||
(log/info "response" response))
|
||||
:on-fail (fn [error]
|
||||
(log/error "Authentication Failed. Error:" error)
|
||||
(when on-auth-fail (on-auth-fail error))
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:theme theme
|
||||
:shell? blur?
|
||||
:content (fn []
|
||||
[enter-password/view
|
||||
{:on-enter-password on-enter-password}])}]))})
|
||||
(do
|
||||
(reset-password)
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:on-close on-close
|
||||
:theme theme
|
||||
:shell? blur?
|
||||
:content (fn []
|
||||
[enter-password/view
|
||||
{:on-enter-password on-enter-password
|
||||
:button-icon-left auth-button-icon-left
|
||||
:button-label auth-button-label}])}]))))))
|
||||
(let [handle-auth-success (fn [biometric?]
|
||||
(fn [entered-password]
|
||||
(let [sha3-pwd (if biometric?
|
||||
(str (security/safe-unmask-data entered-password))
|
||||
(native-module/sha3 (str (security/safe-unmask-data
|
||||
entered-password))))]
|
||||
(on-auth-success sha3-pwd))))
|
||||
password-login (fn [{:keys [on-press-biometrics]}]
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:on-close on-close
|
||||
:theme theme
|
||||
:shell? blur?
|
||||
:content (fn []
|
||||
[enter-password/view
|
||||
{:on-enter-password (handle-auth-success
|
||||
false)
|
||||
:on-press-biometrics on-press-biometrics
|
||||
:button-icon-left auth-button-icon-left
|
||||
:button-label auth-button-label}])}]))
|
||||
; biometrics-login recursively passes itself as a parameter because if the user
|
||||
; fails biometric auth they will be shown the password bottom sheet with an option
|
||||
; to retrigger biometric auth, so they can endlessly repeat this cycle.
|
||||
biometrics-login (fn [on-press-biometrics]
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(biometric/authenticate
|
||||
{:reason (i18n/label :t/biometric-auth-confirm-message)
|
||||
:on-success (fn [_response]
|
||||
(on-close)
|
||||
(rf/dispatch [:standard-auth/on-biometric-success
|
||||
(handle-auth-success true)]))
|
||||
:on-fail (fn [error]
|
||||
(on-close)
|
||||
(log/error "Authentication Failed. Error:" error)
|
||||
(when on-auth-fail (on-auth-fail error))
|
||||
(password-login {:on-press-biometrics
|
||||
#(on-press-biometrics
|
||||
on-press-biometrics)}))}))]
|
||||
(biometric/get-supported-type
|
||||
(fn [biometric-type]
|
||||
(if (and biometric-auth? biometric-type)
|
||||
(biometrics-login biometrics-login)
|
||||
(do
|
||||
(reset-password)
|
||||
(password-login {})))))))
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
(ns status-im2.common.standard-authentication.standard-auth.button.view
|
||||
(:require
|
||||
[quo.core :as quo]
|
||||
[quo.theme :as quo.theme]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.standard-authentication.standard-auth.authorize :as authorize]))
|
||||
|
||||
(defn- view-internal
|
||||
[_]
|
||||
(let [reset-slider? (reagent/atom false)
|
||||
on-close #(reset! reset-slider? true)]
|
||||
(fn [{:keys [biometric-auth?
|
||||
customization-color
|
||||
auth-button-label
|
||||
on-enter-password
|
||||
on-auth-success
|
||||
on-press
|
||||
on-auth-fail
|
||||
auth-button-icon-left
|
||||
size
|
||||
button-label
|
||||
theme
|
||||
blur?
|
||||
container-style
|
||||
icon-left]}]
|
||||
[quo/button
|
||||
{:size size
|
||||
:container-style container-style
|
||||
:customization-color customization-color
|
||||
:icon-left icon-left
|
||||
:on-press (if on-press
|
||||
on-press
|
||||
#(authorize/authorize {:on-close on-close
|
||||
:auth-button-icon-left auth-button-icon-left
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:on-enter-password on-enter-password
|
||||
:biometric-auth? biometric-auth?
|
||||
:on-auth-success on-auth-success
|
||||
:on-auth-fail on-auth-fail
|
||||
:auth-button-label auth-button-label}))}
|
||||
button-label])))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
|
@ -4,17 +4,21 @@
|
|||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.standard-authentication.standard-auth.authorize :as authorize]))
|
||||
[status-im2.common.standard-authentication.standard-auth.authorize :as authorize]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- view-internal
|
||||
[_]
|
||||
(let [reset-slider? (reagent/atom false)
|
||||
on-close #(reset! reset-slider? true)]
|
||||
(fn [{:keys [biometric-auth?
|
||||
track-text
|
||||
(let [reset-slider? (reagent/atom false)
|
||||
on-close (fn []
|
||||
(js/setTimeout
|
||||
#(reset! reset-slider? true)
|
||||
200))
|
||||
auth-method (rf/sub [:auth-method])
|
||||
biometric-auth? (= auth-method "biometric")]
|
||||
(fn [{:keys [track-text
|
||||
customization-color
|
||||
auth-button-label
|
||||
on-enter-password
|
||||
on-auth-success
|
||||
on-auth-fail
|
||||
auth-button-icon-left
|
||||
|
@ -32,7 +36,6 @@
|
|||
:auth-button-icon-left auth-button-icon-left
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:on-enter-password on-enter-password
|
||||
:biometric-auth? biometric-auth?
|
||||
:on-auth-success on-auth-success
|
||||
:on-auth-fail on-auth-fail
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
[status-im2.common.parallax.view :as parallax]
|
||||
[status-im2.common.parallax.whitelist :as whitelist]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im2.common.standard-authentication.core :as standard-auth]
|
||||
[status-im2.contexts.onboarding.enable-biometrics.style :as style]
|
||||
[status-im2.navigation.state :as state]
|
||||
[utils.i18n :as i18n]
|
||||
|
@ -31,13 +30,13 @@
|
|||
(rf/sub [:profile/customization-color]))
|
||||
syncing-results? (= :syncing-results @state/root-id)]
|
||||
[rn/view {:style (style/buttons insets)}
|
||||
[standard-auth/button
|
||||
[quo/button
|
||||
{:size 40
|
||||
:accessibility-label :enable-biometrics-button
|
||||
:icon-left :i/face-id
|
||||
:customization-color profile-color
|
||||
:on-press #(rf/dispatch [:onboarding/enable-biometrics])
|
||||
:button-label (i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})}]
|
||||
:on-press #(rf/dispatch [:onboarding/enable-biometrics])}
|
||||
(i18n/label :t/biometric-enable-button {:bio-type-label bio-type-label})]
|
||||
[quo/button
|
||||
{:accessibility-label :maybe-later-button
|
||||
:background :blur
|
||||
|
|
|
@ -133,13 +133,18 @@
|
|||
{:db (dissoc db :onboarding/profile)
|
||||
:dispatch [:navigate-to-within-stack [:create-profile :new-to-status]]})
|
||||
|
||||
(rf/reg-event-fx :onboarding/set-auth-method
|
||||
(fn [{:keys [db]} [auth-method]]
|
||||
{:db (assoc db :auth-method auth-method)}))
|
||||
|
||||
(rf/defn onboarding-new-account-finalize-setup
|
||||
{:events [:onboarding/finalize-setup]}
|
||||
[{:keys [db]}]
|
||||
(let [masked-password (get-in db [:onboarding/profile :password])
|
||||
key-uid (get-in db [:profile/profile :key-uid])
|
||||
syncing? (get-in db [:onboarding/profile :syncing?])
|
||||
biometric-enabled? (= (get-in db [:onboarding/profile :auth-method])
|
||||
auth-method (get-in db [:onboarding/profile :auth-method])
|
||||
biometric-enabled? (= auth-method
|
||||
constants/auth-method-biometric)]
|
||||
(cond-> {:db (assoc db :onboarding/generated-keys? true)}
|
||||
biometric-enabled?
|
||||
|
@ -149,9 +154,9 @@
|
|||
masked-password
|
||||
(security/hash-masked-password masked-password))
|
||||
:on-success (fn []
|
||||
(if syncing?
|
||||
(rf/dispatch [:onboarding/navigate-to-enable-notifications])
|
||||
(log/error "successfully saved biometrics")))
|
||||
(rf/dispatch [:onboarding/set-auth-method auth-method])
|
||||
(when syncing?
|
||||
(rf/dispatch [:onboarding/navigate-to-enable-notifications])))
|
||||
:on-error #(log/error "failed to save biometrics"
|
||||
{:key-uid key-uid
|
||||
:error %})}))))
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.confirmation-drawer.view :as confirmation-drawer]
|
||||
[status-im2.common.standard-authentication.password-input.view :as password-input]
|
||||
[status-im2.common.standard-authentication.core :as standard-authentication]
|
||||
[status-im2.config :as config]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.onboarding.common.background.view :as background]
|
||||
|
@ -205,7 +205,7 @@
|
|||
:customization-color (or customization-color :primary)
|
||||
:profile-picture profile-picture
|
||||
:card-style style/login-profile-card}]
|
||||
[password-input/view
|
||||
[standard-authentication/password-input
|
||||
{:shell? true
|
||||
:default-password password}]]
|
||||
[quo/button
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
[status-im2.contexts.syncing.utils :as sync-utils]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(rf/defn local-pairing-update-role
|
||||
|
@ -97,7 +96,7 @@
|
|||
|
||||
(rf/defn preparations-for-connection-string
|
||||
{:events [:syncing/get-connection-string]}
|
||||
[{:keys [db] :as cofx} entered-password on-valid-connection-string]
|
||||
[{:keys [db] :as cofx} sha3-pwd on-valid-connection-string]
|
||||
(let [error (get-in db [:profile/login :error])
|
||||
handle-connection (fn [response]
|
||||
(when (sync-utils/valid-connection-string? response)
|
||||
|
@ -105,8 +104,7 @@
|
|||
(rf/dispatch [:syncing/update-role constants/local-pairing-role-sender])
|
||||
(rf/dispatch [:hide-bottom-sheet])))]
|
||||
(when-not (and error (string/blank? error))
|
||||
(let [key-uid (get-in db [:profile/login :key-uid])
|
||||
sha3-pwd (native-module/sha3 (str (security/safe-unmask-data entered-password)))
|
||||
(let [key-uid (get-in db [:profile/profile :key-uid])
|
||||
config-map (.stringify js/JSON
|
||||
(clj->js {:senderConfig {:keyUID key-uid
|
||||
:keystorePath ""
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
(reset! code nil)
|
||||
(reset! timestamp nil)
|
||||
(reset! valid-for-ms code-valid-for-ms))
|
||||
on-enter-password (fn [entered-password]
|
||||
on-auth-success (fn [entered-password]
|
||||
(rf/dispatch [:syncing/get-connection-string entered-password
|
||||
set-code]))]
|
||||
(fn []
|
||||
|
@ -124,8 +124,7 @@
|
|||
:size :size-40
|
||||
:track-text (i18n/label :t/slide-to-reveal-code)
|
||||
:customization-color customization-color
|
||||
:on-enter-password on-enter-password
|
||||
:biometric-auth? false
|
||||
:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/reveal-sync-code)
|
||||
:auth-button-icon-left :i/reveal}]])]]
|
||||
[rn/view {:style style/sync-code}
|
||||
|
|
|
@ -43,8 +43,8 @@
|
|||
:bottom-action-props {:customization-color @account-color
|
||||
:disabled? (string/blank? @account-name)
|
||||
:on-press #(rf/dispatch [:wallet/add-account
|
||||
nil
|
||||
{:type :watch
|
||||
{:sha3-pwd nil
|
||||
:type :watch
|
||||
:account-name @account-name
|
||||
:emoji @account-emoji
|
||||
:color @account-color}
|
||||
|
|
|
@ -118,14 +118,14 @@
|
|||
{:size :size-48
|
||||
:track-text (i18n/label :t/slide-to-create-account)
|
||||
:customization-color @account-color
|
||||
:on-enter-password (fn [entered-password]
|
||||
:on-auth-success (fn [entered-password]
|
||||
(prn entered-password)
|
||||
(rf/dispatch [:wallet/derive-address-and-add-account
|
||||
entered-password
|
||||
{:emoji @emoji
|
||||
{:sha3-pwd entered-password
|
||||
:emoji @emoji
|
||||
:color @account-color
|
||||
:path @derivation-path
|
||||
:account-name @account-name}]))
|
||||
:biometric-auth? false
|
||||
:auth-button-label (i18n/label :t/confirm)
|
||||
:container-style (style/slide-button-container bottom)}]])))
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
[camel-snake-kebab.core :as csk]
|
||||
[camel-snake-kebab.extras :as cske]
|
||||
[clojure.string :as string]
|
||||
[native-module.core :as native-module]
|
||||
[quo.foundations.colors :as colors]
|
||||
[react-native.background-timer :as background-timer]
|
||||
[status-im2.common.data-store.wallet :as data-store]
|
||||
|
@ -15,7 +14,6 @@
|
|||
[utils.i18n :as i18n]
|
||||
[utils.number]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]
|
||||
[utils.transforms :as types]))
|
||||
|
||||
(rf/reg-event-fx :wallet/show-account-created-toast
|
||||
|
@ -143,9 +141,8 @@
|
|||
{:db (dissoc db :wallet/scanned-address :wallet/send-address)})
|
||||
|
||||
(rf/reg-event-fx :wallet/create-derived-addresses
|
||||
(fn [{:keys [db]} [password {:keys [path]} on-success]]
|
||||
(let [{:keys [wallet-root-address]} (:profile/profile db)
|
||||
sha3-pwd (native-module/sha3 (str (security/safe-unmask-data password)))]
|
||||
(fn [{:keys [db]} [{:keys [sha3-pwd path]} on-success]]
|
||||
(let [{:keys [wallet-root-address]} (:profile/profile db)]
|
||||
{:fx [[:json-rpc/call
|
||||
[{:method "wallet_getDerivedAddresses"
|
||||
:params [sha3-pwd wallet-root-address [path]]
|
||||
|
@ -162,11 +159,10 @@
|
|||
|
||||
(rf/reg-event-fx :wallet/add-account
|
||||
(fn [{:keys [db]}
|
||||
[password {:keys [emoji account-name color type] :or {type :generated}}
|
||||
[{: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)
|
||||
key-uid (get-in db [:profile/profile :key-uid])
|
||||
sha3-pwd (native-module/sha3 (security/safe-unmask-data password))
|
||||
account-config {:key-uid (when (= type :generated) key-uid)
|
||||
:wallet false
|
||||
:chat false
|
||||
|
@ -185,11 +181,11 @@
|
|||
|
||||
(rf/reg-event-fx
|
||||
:wallet/derive-address-and-add-account
|
||||
(fn [_ [password account-details]]
|
||||
(fn [_ [account-details]]
|
||||
(let [on-success (fn [derived-address-details]
|
||||
(rf/dispatch [:wallet/add-account password account-details
|
||||
(rf/dispatch [:wallet/add-account account-details
|
||||
(first derived-address-details)]))]
|
||||
{:fx [[:dispatch [:wallet/create-derived-addresses password account-details on-success]]]})))
|
||||
{:fx [[:dispatch [:wallet/create-derived-addresses account-details on-success]]]})))
|
||||
|
||||
(rf/defn get-ethereum-chains
|
||||
{:events [:wallet/get-ethereum-chains]}
|
||||
|
|
Loading…
Reference in New Issue