Wallet: Derivation Path - Scanning for Activity (#19482)
Wallet: Derivation Path - Scanning for Activity (#19482)
This commit is contained in:
parent
0e29149363
commit
74e84644aa
Binary file not shown.
After Width: | Height: | Size: 750 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 595 B |
Binary file not shown.
After Width: | Height: | Size: 889 B |
|
@ -83,7 +83,7 @@
|
||||||
:has-activity {:accessibility-label :account-has-activity
|
:has-activity {:accessibility-label :account-has-activity
|
||||||
:icon :i/done
|
:icon :i/done
|
||||||
:type :success
|
:type :success
|
||||||
:message :t/this-address-has-activity}
|
:message :t/address-activity}
|
||||||
:no-activity {:accessibility-label :account-has-no-activity
|
:no-activity {:accessibility-label :account-has-no-activity
|
||||||
:icon :i/info
|
:icon :i/info
|
||||||
:type :warning
|
:type :warning
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
(ns status-im.contexts.wallet.create-account.edit-derivation-path.component-spec
|
|
||||||
(:require
|
|
||||||
[status-im.contexts.wallet.create-account.edit-derivation-path.view :as edit-derivation-path]
|
|
||||||
[test-helpers.component :as h]))
|
|
||||||
|
|
||||||
(def sub-mocks
|
|
||||||
{:profile/profile {:public-key "123"}
|
|
||||||
:contacts/contact-two-names-by-identity ["a"]
|
|
||||||
:profile/image "image"})
|
|
||||||
|
|
||||||
(h/describe "Edit derivation path page"
|
|
||||||
(h/test "Default render"
|
|
||||||
(h/setup-subs sub-mocks)
|
|
||||||
(h/render-with-theme-provider [edit-derivation-path/view {}])
|
|
||||||
(h/is-truthy (h/get-by-translation-text :t/edit-derivation-path))
|
|
||||||
(h/is-truthy (h/get-by-translation-text :t/path-format))
|
|
||||||
(h/is-truthy (h/get-by-translation-text :t/derivation-path))
|
|
||||||
(h/is-truthy (h/get-by-translation-text :t/reveal-address))
|
|
||||||
(h/is-truthy (h/get-by-translation-text :t/save)))
|
|
||||||
|
|
||||||
|
|
||||||
(h/test "Reveal address pressed"
|
|
||||||
(h/setup-subs sub-mocks)
|
|
||||||
(let [on-reveal (h/mock-fn)]
|
|
||||||
(h/render-with-theme-provider [edit-derivation-path/view {:on-reveal on-reveal}])
|
|
||||||
(h/fire-event :press (h/get-by-translation-text :t/reveal-address))
|
|
||||||
(h/was-called on-reveal)
|
|
||||||
(h/wait-for #(h/is-truthy (h/get-by-translation-text :t/address-activity)))))
|
|
||||||
|
|
||||||
(h/test "Reset button pressed"
|
|
||||||
(h/setup-subs sub-mocks)
|
|
||||||
(let [on-reset (h/mock-fn)]
|
|
||||||
(h/render-with-theme-provider [edit-derivation-path/view {:on-reset on-reset}])
|
|
||||||
(h/fire-event :press (h/get-by-translation-text :t/reset))
|
|
||||||
(h/was-called on-reset)
|
|
||||||
(h/wait-for #(h/is-truthy (h/get-by-translation-text :t/derive-addresses))))))
|
|
|
@ -30,9 +30,11 @@
|
||||||
:padding-top 24})
|
:padding-top 24})
|
||||||
|
|
||||||
(defn revealed-address
|
(defn revealed-address
|
||||||
[theme]
|
[state theme]
|
||||||
{:border-width 1
|
{:border-width 1
|
||||||
:border-color (colors/resolve-color :success theme 40)
|
:border-color (if (= state :scanning)
|
||||||
|
(colors/theme-colors colors/neutral-20 colors/neutral-70 theme)
|
||||||
|
(colors/resolve-color (if (= state :has-activity) :success :warning) theme 40))
|
||||||
:border-style :dashed
|
:border-style :dashed
|
||||||
:border-radius 16
|
:border-radius 16
|
||||||
:padding-horizontal 12
|
:padding-horizontal 12
|
||||||
|
@ -42,8 +44,6 @@
|
||||||
{:margin-vertical 9
|
{:margin-vertical 9
|
||||||
:padding-left 2})
|
:padding-left 2})
|
||||||
|
|
||||||
(def temporal-placeholder
|
(defn keyboard
|
||||||
{:height 94
|
[padding-bottom]
|
||||||
:background-color colors/danger-50
|
{:padding-bottom padding-bottom})
|
||||||
:align-items :center
|
|
||||||
:justify-content :center})
|
|
||||||
|
|
|
@ -1,51 +1,68 @@
|
||||||
(ns status-im.contexts.wallet.create-account.edit-derivation-path.view
|
(ns status-im.contexts.wallet.create-account.edit-derivation-path.view
|
||||||
(:require
|
(:require
|
||||||
|
[clojure.string :as string]
|
||||||
[quo.core :as quo]
|
[quo.core :as quo]
|
||||||
[quo.foundations.colors :as colors]
|
[quo.foundations.colors :as colors]
|
||||||
[quo.theme :as quo.theme]
|
[quo.theme :as quo.theme]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.safe-area :as safe-area]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im.contexts.wallet.common.temp :as temp]
|
|
||||||
[status-im.contexts.wallet.common.utils :as utils]
|
[status-im.contexts.wallet.common.utils :as utils]
|
||||||
[status-im.contexts.wallet.create-account.edit-derivation-path.path-format-sheet.view :as
|
[status-im.contexts.wallet.create-account.edit-derivation-path.path-format-sheet.view :as
|
||||||
path-format-sheet]
|
path-format-sheet]
|
||||||
[status-im.contexts.wallet.create-account.edit-derivation-path.style :as style]
|
[status-im.contexts.wallet.create-account.edit-derivation-path.style :as style]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]
|
||||||
|
[utils.security.core :as security]))
|
||||||
|
|
||||||
(defn- view-internal
|
(defn- view-internal
|
||||||
"States:
|
"States:
|
||||||
default(filled)
|
default(filled)
|
||||||
| -> (reveal-action) -> show
|
| -> (reveal-action) -> show
|
||||||
| -> (clear-action) -> empty -> (derive-action) -> choose -> (choose-action) -> show"
|
| -> (clear-action) -> empty -> (derive-action) -> choose -> (choose-action) -> show"
|
||||||
[{:keys [on-reset on-reveal]}]
|
[{:keys [on-reset]}]
|
||||||
(let [top (safe-area/get-top)
|
(let [top (safe-area/get-top)
|
||||||
bottom (safe-area/get-bottom)
|
bottom (safe-area/get-bottom)
|
||||||
state (reagent/atom :default)
|
input-focused? (reagent/atom false)
|
||||||
reveal-action (fn [_]
|
path-value (reagent/atom "")
|
||||||
(reset! state :show)
|
input-ref (reagent/atom nil)
|
||||||
(when on-reveal
|
|
||||||
(on-reveal)))
|
|
||||||
clear-action #(reset! state :empty)
|
|
||||||
derive-action #(js/alert "To be implemented")
|
|
||||||
choose-action #(reset! state :show)
|
|
||||||
path-value (reagent/atom (utils/get-formatted-derivation-path 3))
|
|
||||||
handle-path-change (fn [v]
|
|
||||||
(reset! path-value v)
|
|
||||||
(when (empty? v)
|
|
||||||
(clear-action)))
|
|
||||||
reset-path-value (fn [_]
|
reset-path-value (fn [_]
|
||||||
(reset! path-value "")
|
(reset! path-value "")
|
||||||
(clear-action)
|
|
||||||
(when on-reset
|
(when on-reset
|
||||||
(on-reset)))]
|
(on-reset)))]
|
||||||
(fn [{:keys [theme]}]
|
(fn [{:keys [theme]}]
|
||||||
(let [{:keys [public-key]} (rf/sub [:profile/profile])
|
(let [{:keys [public-key address]} (rf/sub [:profile/profile])
|
||||||
primary-name (first (rf/sub [:contacts/contact-two-names-by-identity
|
{:keys [password current-derivation-path]} (rf/sub [:get-screen-params])
|
||||||
|
primary-name (first (rf/sub
|
||||||
|
[:contacts/contact-two-names-by-identity
|
||||||
public-key]))
|
public-key]))
|
||||||
profile-picture (rf/sub [:profile/image])
|
profile-picture (rf/sub [:profile/image])
|
||||||
show-path-format-sheet #(rf/dispatch [:show-bottom-sheet {:content path-format-sheet/view}])]
|
show-path-format-sheet #(rf/dispatch [:show-bottom-sheet
|
||||||
|
{:content path-format-sheet/view}])
|
||||||
|
derivation-path (rf/sub [:wallet/derivation-path])
|
||||||
|
state (rf/sub [:wallet/derivation-path-state])
|
||||||
|
navigate-back-handler #(if @input-focused?
|
||||||
|
(do
|
||||||
|
(.blur ^js @input-ref)
|
||||||
|
true)
|
||||||
|
(rf/dispatch [:navigate-to
|
||||||
|
:screen/wallet.create-account]))
|
||||||
|
on-change-text #(rf/dispatch
|
||||||
|
[:wallet/get-derived-addresses
|
||||||
|
{:password (security/safe-unmask-data
|
||||||
|
password)
|
||||||
|
:paths [(string/replace @path-value
|
||||||
|
#"\s"
|
||||||
|
"")]
|
||||||
|
:derived-from address}])]
|
||||||
|
(rn/use-mount (fn []
|
||||||
|
(reset! path-value current-derivation-path)
|
||||||
|
(rf/dispatch [:wallet/get-derived-addresses
|
||||||
|
{:password (security/safe-unmask-data password)
|
||||||
|
:paths [(string/replace @path-value #"\s" "")]
|
||||||
|
:derived-from address}])
|
||||||
|
(rn/hw-back-add-listener navigate-back-handler)
|
||||||
|
#(rn/hw-back-remove-listener navigate-back-handler)))
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style (style/screen top)}
|
{:style (style/screen top)}
|
||||||
[quo/page-nav
|
[quo/page-nav
|
||||||
|
@ -82,54 +99,33 @@
|
||||||
:value (i18n/label :t/default-format)
|
:value (i18n/label :t/default-format)
|
||||||
:container-style {:margin-horizontal 20}}]]
|
:container-style {:margin-horizontal 20}}]]
|
||||||
[quo/input
|
[quo/input
|
||||||
{:container-style style/input-container
|
{:ref #(reset! input-ref %)
|
||||||
|
:container-style style/input-container
|
||||||
:value @path-value
|
:value @path-value
|
||||||
:editable false
|
:on-focus #(reset! input-focused? true)
|
||||||
|
:on-blur #(reset! input-focused? false)
|
||||||
|
:show-soft-input-on-focus false
|
||||||
|
:editable true
|
||||||
:label (i18n/label :t/derivation-path)
|
:label (i18n/label :t/derivation-path)
|
||||||
:placeholder (utils/get-formatted-derivation-path 3)
|
:placeholder (utils/get-formatted-derivation-path 3)
|
||||||
:button {:on-press reset-path-value
|
:button {:on-press reset-path-value
|
||||||
:text (i18n/label :t/reset)}
|
:text (i18n/label :t/reset)}}]
|
||||||
:on-change-text handle-path-change}]
|
|
||||||
|
|
||||||
(case @state
|
|
||||||
:default
|
|
||||||
[quo/bottom-actions
|
|
||||||
{:actions :one-action
|
|
||||||
:button-one-label (i18n/label :t/reveal-address)
|
|
||||||
:button-one-props {:type :outline
|
|
||||||
:icon-left :i/keycard-card
|
|
||||||
:on-press reveal-action}}]
|
|
||||||
|
|
||||||
:empty
|
|
||||||
[quo/bottom-actions
|
|
||||||
{:actions :one-action
|
|
||||||
:button-one-label (i18n/label :t/derive-addresses)
|
|
||||||
:button-one-props {:type :outline
|
|
||||||
:icon-left :i/keycard-card
|
|
||||||
:on-press derive-action}}]
|
|
||||||
|
|
||||||
:show
|
|
||||||
[rn/view {:style style/revealed-address-container}
|
[rn/view {:style style/revealed-address-container}
|
||||||
[rn/view {:style (style/revealed-address theme)}
|
[rn/view {:style (style/revealed-address state theme)}
|
||||||
[quo/text
|
[quo/text
|
||||||
{:weight :monospace}
|
{:weight :monospace}
|
||||||
temp/address]]
|
(:address derivation-path)]]
|
||||||
[quo/info-message
|
[quo/info-message
|
||||||
{:type :success
|
{:type (case state
|
||||||
:icon :i/done
|
:has-activity :success
|
||||||
|
:no-activity :warning
|
||||||
|
:default)
|
||||||
|
:icon (if (= state :scanning) :i/scanning :i/done)
|
||||||
:style style/info}
|
:style style/info}
|
||||||
(i18n/label :t/address-activity)]]
|
(i18n/label (case state
|
||||||
|
:has-activity :t/address-activity
|
||||||
:choose
|
:no-activity :t/address-no-activity
|
||||||
[rn/view {:style style/temporal-placeholder}
|
:t/scanning))]]
|
||||||
[quo/text "Dropdown input will be here"]
|
|
||||||
[quo/button
|
|
||||||
{:on-press (fn [_]
|
|
||||||
(reset! path-value (utils/get-formatted-derivation-path 1))
|
|
||||||
(choose-action))}
|
|
||||||
"Choose"]]
|
|
||||||
nil)
|
|
||||||
|
|
||||||
[rn/view {:style (style/save-button-container bottom)}
|
[rn/view {:style (style/save-button-container bottom)}
|
||||||
[quo/bottom-actions
|
[quo/bottom-actions
|
||||||
{:actions :one-action
|
{:actions :one-action
|
||||||
|
@ -137,9 +133,17 @@
|
||||||
:button-one-props {:type :primary
|
:button-one-props {:type :primary
|
||||||
:on-press #(js/alert "Save!")
|
:on-press #(js/alert "Save!")
|
||||||
:disabled? true}}]]
|
:disabled? true}}]]
|
||||||
(when-not (= @state :show)
|
(when @input-focused?
|
||||||
[quo/numbered-keyboard
|
[quo/numbered-keyboard
|
||||||
{:left-action :dot
|
{:left-action :dot
|
||||||
:delete-key? true}])]))))
|
:delete-key? true
|
||||||
|
:container-style (style/keyboard bottom)
|
||||||
|
:on-press (fn [value]
|
||||||
|
(reset! path-value (str @path-value value))
|
||||||
|
(on-change-text))
|
||||||
|
:on-delete (fn []
|
||||||
|
(reset! path-value (subs @path-value 0 (dec (count @path-value))))
|
||||||
|
(on-change-text))}])]))))
|
||||||
|
|
||||||
(def view (quo.theme/with-theme view-internal))
|
(def view (quo.theme/with-theme view-internal))
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
(ns status-im.contexts.wallet.create-account.events
|
(ns status-im.contexts.wallet.create-account.events
|
||||||
(:require [status-im.contexts.wallet.data-store :as data-store]
|
(:require [camel-snake-kebab.extras :as cske]
|
||||||
[utils.re-frame :as rf]))
|
[status-im.contexts.wallet.data-store :as data-store]
|
||||||
|
[utils.re-frame :as rf]
|
||||||
|
[utils.transforms :as transforms]))
|
||||||
|
|
||||||
(defn get-keypairs-success
|
(defn get-keypairs-success
|
||||||
[{:keys [db]} [keypairs]]
|
[{:keys [db]} [keypairs]]
|
||||||
|
@ -49,3 +51,23 @@
|
||||||
{:db (update-in db [:wallet :ui :create-account] dissoc :new-keypair)})
|
{: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-new-keypair clear-new-keypair)
|
||||||
|
|
||||||
|
(defn get-derived-addresses
|
||||||
|
[{:keys [db]} [{:keys [password derived-from paths]}]]
|
||||||
|
{:db (assoc-in db [:wallet :ui :create-account :derivation-path-state] :scanning)
|
||||||
|
:json-rpc/call [{:method "wallet_getDerivedAddresses"
|
||||||
|
:params [password derived-from paths]
|
||||||
|
:on-success [:wallet/get-derived-addresses-success]}]})
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/get-derived-addresses get-derived-addresses)
|
||||||
|
|
||||||
|
(defn get-derived-addresses-success
|
||||||
|
[{:keys [db]} [response]]
|
||||||
|
(let [derived-address (first response)]
|
||||||
|
{:db (-> db
|
||||||
|
(assoc-in [:wallet :ui :create-account :derivation-path-state]
|
||||||
|
(if (:has-activity derived-address) :has-activity :no-activity))
|
||||||
|
(assoc-in [:wallet :ui :create-account :derivation-path]
|
||||||
|
(cske/transform-keys transforms/->kebab-case-keyword derived-address)))}))
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/get-derived-addresses-success get-derived-addresses-success)
|
||||||
|
|
|
@ -42,3 +42,14 @@
|
||||||
expected-db {:wallet {:ui {:create-account {}}}}
|
expected-db {:wallet {:ui {:create-account {}}}}
|
||||||
effects (events/clear-new-keypair {:db db})]
|
effects (events/clear-new-keypair {:db db})]
|
||||||
(is (match? (:db effects) expected-db))))
|
(is (match? (:db effects) expected-db))))
|
||||||
|
|
||||||
|
(deftest get-derived-addresses-test
|
||||||
|
(let [db {}
|
||||||
|
password "test-password"
|
||||||
|
derived-from "test-derive-from"
|
||||||
|
paths ["path1"]
|
||||||
|
event-args [{:password password :derived-from derived-from :paths paths}]
|
||||||
|
expected-db (assoc-in db [:wallet :ui :create-account :derivation-path-state] :scanning)
|
||||||
|
effects (events/get-derived-addresses {:db db} event-args)
|
||||||
|
result-db (:db effects)]
|
||||||
|
(is (match? result-db expected-db))))
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[status-im.contexts.wallet.create-account.style :as style]
|
[status-im.contexts.wallet.create-account.style :as style]
|
||||||
[status-im.contexts.wallet.create-account.utils :as create-account.utils]
|
[status-im.contexts.wallet.create-account.utils :as create-account.utils]
|
||||||
[status-im.contexts.wallet.sheets.account-origin.view :as account-origin]
|
[status-im.contexts.wallet.sheets.account-origin.view :as account-origin]
|
||||||
|
[status-im.feature-flags :as ff]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[utils.responsiveness :as responsiveness]
|
[utils.responsiveness :as responsiveness]
|
||||||
|
@ -23,6 +24,12 @@
|
||||||
|
|
||||||
(defn- get-keypair-data
|
(defn- get-keypair-data
|
||||||
[{:keys [title primary-keypair? new-keypair? derivation-path customization-color]}]
|
[{:keys [title primary-keypair? new-keypair? derivation-path customization-color]}]
|
||||||
|
(let [formatted-path (string/replace derivation-path #"/" " / ")
|
||||||
|
on-auth-success (fn [password]
|
||||||
|
(rf/dispatch [:navigate-to
|
||||||
|
:screen/wallet.edit-derivation-path
|
||||||
|
{:password password
|
||||||
|
:current-derivation-path formatted-path}]))]
|
||||||
[{:title title
|
[{:title title
|
||||||
:image (if primary-keypair? :avatar :icon)
|
:image (if primary-keypair? :avatar :icon)
|
||||||
:image-props (if primary-keypair?
|
:image-props (if primary-keypair?
|
||||||
|
@ -40,12 +47,16 @@
|
||||||
:image :icon
|
:image :icon
|
||||||
:image-props :i/derivated-path
|
:image-props :i/derivated-path
|
||||||
:action :button
|
:action :button
|
||||||
:action-props {:on-press #(js/alert "Coming soon!")
|
:action-props {:on-press #(if (ff/enabled? :ff/wallet.edit-derivation-path)
|
||||||
|
(rf/dispatch [:standard-auth/authorize
|
||||||
|
{:on-auth-success on-auth-success
|
||||||
|
:auth-button-label (i18n/label :t/continue)}])
|
||||||
|
(js/alert "Coming soon!"))
|
||||||
:button-text (i18n/label :t/edit)
|
:button-text (i18n/label :t/edit)
|
||||||
:icon-left :i/placeholder
|
:icon-left :i/face-id
|
||||||
:alignment :flex-start}
|
:alignment :flex-start}
|
||||||
:description :text
|
:description :text
|
||||||
:description-props {:text (string/replace derivation-path #"/" " / ")}}])
|
:description-props {:text formatted-path}}]))
|
||||||
|
|
||||||
(defn- f-view
|
(defn- f-view
|
||||||
[_]
|
[_]
|
||||||
|
|
|
@ -6,5 +6,4 @@
|
||||||
[status-im.contexts.shell.share.wallet.component-spec]
|
[status-im.contexts.shell.share.wallet.component-spec]
|
||||||
[status-im.contexts.wallet.add-address-to-watch.component-spec]
|
[status-im.contexts.wallet.add-address-to-watch.component-spec]
|
||||||
[status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
|
[status-im.contexts.wallet.add-address-to-watch.confirm-address.component-spec]
|
||||||
[status-im.contexts.wallet.create-account.edit-derivation-path.component-spec]
|
|
||||||
[status-im.contexts.wallet.send.input-amount.component-spec]))
|
[status-im.contexts.wallet.send.input-amount.component-spec]))
|
||||||
|
|
|
@ -137,6 +137,11 @@
|
||||||
:goerli-enabled? goerli-enabled?})
|
:goerli-enabled? goerli-enabled?})
|
||||||
selected-networks))))
|
selected-networks))))
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/derivation-path-state
|
||||||
|
:<- [:wallet/create-account]
|
||||||
|
:-> :derivation-path-state)
|
||||||
|
|
||||||
(rf/reg-sub
|
(rf/reg-sub
|
||||||
:wallet/accounts
|
:wallet/accounts
|
||||||
:<- [:wallet]
|
:<- [:wallet]
|
||||||
|
|
|
@ -2458,6 +2458,8 @@
|
||||||
"edit-wallet-network-preferences-updated-message": "Account network preferences has been updated",
|
"edit-wallet-network-preferences-updated-message": "Account network preferences has been updated",
|
||||||
"search-assets": "Search assets",
|
"search-assets": "Search assets",
|
||||||
"address-activity": "This address has activity",
|
"address-activity": "This address has activity",
|
||||||
|
"address-no-activity": "This address has no activity",
|
||||||
|
"scanning": "Scanning for activity...",
|
||||||
"keypairs": "Keypairs",
|
"keypairs": "Keypairs",
|
||||||
"keypairs-description": "Select keypair to derive your new account from",
|
"keypairs-description": "Select keypair to derive your new account from",
|
||||||
"confirm-account-origin": "Confirm account origin",
|
"confirm-account-origin": "Confirm account origin",
|
||||||
|
@ -2481,7 +2483,6 @@
|
||||||
"est-time": "Est. time",
|
"est-time": "Est. time",
|
||||||
"user-gets": "{{name}} gets",
|
"user-gets": "{{name}} gets",
|
||||||
"slide-to-send": "Slide to send",
|
"slide-to-send": "Slide to send",
|
||||||
"this-address-has-activity": "This address has activity",
|
|
||||||
"generate-new-keypair": "Generate new keypair",
|
"generate-new-keypair": "Generate new keypair",
|
||||||
"import-using-phrase": "Import using recovery phrase",
|
"import-using-phrase": "Import using recovery phrase",
|
||||||
"import-from-keycard": "Import from Keycard",
|
"import-from-keycard": "Import from Keycard",
|
||||||
|
|
Loading…
Reference in New Issue