[#9749] Support importing private key and seed
This commit is contained in:
parent
c9486dc634
commit
a79a72fccb
|
@ -620,5 +620,6 @@ var TopLevel = {
|
|||
"multiAccountLoadAccount" : function () {},
|
||||
"multiAccountStoreAccount" : function () {},
|
||||
"multiAccountImportMnemonic" : function () {},
|
||||
"multiAccountImportPrivateKey" : function () {},
|
||||
"validateMnemonic" : function () {}
|
||||
}
|
||||
|
|
|
@ -776,6 +776,23 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
StatusThreadPoolExecutor.getInstance().execute(r);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void multiAccountImportPrivateKey(final String json, final Callback callback) {
|
||||
Log.d(TAG, "multiAccountImportPrivateKey");
|
||||
if (!checkAvailability()) {
|
||||
callback.invoke(false);
|
||||
return;
|
||||
}
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String res = Statusgo.multiAccountImportPrivateKey(json);
|
||||
callback.invoke(res);
|
||||
}
|
||||
};
|
||||
StatusThreadPoolExecutor.getInstance().execute(r);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hashTransaction(final String txArgsJSON, final Callback callback) {
|
||||
Log.d(TAG, "hashTransaction");
|
||||
|
|
|
@ -266,6 +266,16 @@ RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
|
|||
callback(@[result]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////// multiAccountImportPrivateKey
|
||||
RCT_EXPORT_METHOD(multiAccountImportPrivateKey:(NSString *)json
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"MultiAccountImportPrivateKey() method called");
|
||||
#endif
|
||||
NSString *result = StatusgoMultiAccountImportPrivateKey(json);
|
||||
callback(@[result]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////// multiAccountImportMnemonic
|
||||
RCT_EXPORT_METHOD(multiAccountImportMnemonic:(NSString *)json
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
|
|
|
@ -18,13 +18,11 @@
|
|||
(if card-connected?
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:hardwallet :on-export-success]
|
||||
#(vector :wallet.accounts/account-generated
|
||||
{:name (str "Account " path-num)
|
||||
;; Strip leading 04 prefix denoting uncompressed key format
|
||||
#(vector :wallet.accounts/account-stored
|
||||
{;; Strip leading 04 prefix denoting uncompressed key format
|
||||
:address (eip55/address->checksum (str "0x" (ethereum/public-key->address (subs % 2))))
|
||||
:public-key (str "0x" %)
|
||||
:path path
|
||||
:color (rand-nth colors/account-colors)}))
|
||||
:path path}))
|
||||
:hardwallet/export-key {:pin pin :pairing pairing :path path}}
|
||||
(navigation/navigate-to-cofx :keycard-processing nil)
|
||||
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account))
|
||||
|
|
|
@ -157,9 +157,15 @@
|
|||
(types/clj->json {:mnemonicPhrase mnemonic
|
||||
;;NOTE this is not the multiaccount password
|
||||
:Bip39Passphrase password})
|
||||
|
||||
callback))
|
||||
|
||||
(defn multiaccount-import-private-key
|
||||
[private-key callback]
|
||||
(log/debug "[native-module] multiaccount-import-private-key")
|
||||
(.multiAccountImportPrivateKey (status)
|
||||
(types/clj->json {:privateKey private-key})
|
||||
callback))
|
||||
|
||||
(defn verify
|
||||
"NOTE: beware, the password has to be sha3 hashed"
|
||||
[address hashed-password callback]
|
||||
|
|
|
@ -538,15 +538,19 @@
|
|||
:add-account-disabled?
|
||||
:<- [:multiaccount/accounts]
|
||||
:<- [:add-account]
|
||||
(fn [[accounts {:keys [address]}]]
|
||||
(or (not (ethereum/address? address))
|
||||
(some #(when (= (:address %) address) %) accounts))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:add-account-scanned-address
|
||||
:<- [:add-account]
|
||||
(fn [add-account]
|
||||
(get add-account :scanned-address)))
|
||||
(fn [[accounts {:keys [address type account seed private-key]}]]
|
||||
(or (string/blank? (:name account))
|
||||
(case type
|
||||
:generate
|
||||
false
|
||||
:watch
|
||||
(or (not (ethereum/address? address))
|
||||
(some #(when (= (:address %) address) %) accounts))
|
||||
:key
|
||||
(string/blank? (security/safe-unmask-data private-key))
|
||||
:seed
|
||||
(string/blank? (security/safe-unmask-data seed))
|
||||
false))))
|
||||
|
||||
;;CHAT ==============================================================================================================
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
:new-public-chat :default
|
||||
:wallet-account :default
|
||||
:add-new-account :default
|
||||
:add-watch-account :default
|
||||
:add-new-account-password :default
|
||||
:add-new-account-pin :default
|
||||
:about-app :default
|
||||
:help-center :default
|
||||
|
|
|
@ -184,10 +184,7 @@
|
|||
:welcome [:modal home/welcome]
|
||||
:keycard-welcome keycard/welcome
|
||||
:add-new-account add-account/add-account
|
||||
:add-watch-account add-account/add-watch-account
|
||||
:add-new-account-password add-account/password
|
||||
:add-new-account-pin add-account/pin
|
||||
:account-added account-settings/account-added
|
||||
:account-settings account-settings/account-settings})
|
||||
|
||||
(defn get-screen [screen]
|
||||
|
|
|
@ -6,10 +6,7 @@
|
|||
:screens (cond-> [:wallet
|
||||
:wallet-account
|
||||
:add-new-account
|
||||
:add-watch-account
|
||||
:add-new-account-password
|
||||
:add-new-account-pin
|
||||
:account-added
|
||||
:account-settings
|
||||
:collectibles-list
|
||||
:wallet-onboarding-setup
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.button :as button]
|
||||
[clojure.string :as string]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.copyable-text :as copyable-text]
|
||||
[reagent.core :as reagent]
|
||||
|
@ -33,45 +31,6 @@
|
|||
:label (i18n/label :t/cancel)
|
||||
:type :secondary}}]]))
|
||||
|
||||
(defview account-added []
|
||||
(letsubs [{:keys [account]} [:add-account]]
|
||||
[react/keyboard-avoiding-view {:flex 1}
|
||||
[react/scroll-view {:keyboard-should-persist-taps :handled
|
||||
:style {:margin-top 70 :flex 1}}
|
||||
[react/view {:align-items :center :padding-horizontal 40}
|
||||
[react/view {:height 40 :width 40 :border-radius 20 :align-items :center :justify-content :center
|
||||
:background-color (:color account)}
|
||||
[icons/icon :main-icons/check {:color colors/white}]]
|
||||
[react/text {:style {:typography :header :margin-top 16}}
|
||||
(i18n/label :t/account-added)]
|
||||
[react/text {:style {:color colors/gray :text-align :center :margin-top 16 :line-height 22}}
|
||||
(i18n/label :t/you-can-change-account)]]
|
||||
[react/view {:height 52}]
|
||||
[react/view {:margin-horizontal 16}
|
||||
[text-input/text-input-with-label
|
||||
{:label (i18n/label :t/account-name)
|
||||
:auto-focus false
|
||||
:default-value (:name account)
|
||||
:placeholder (i18n/label :t/account-name)
|
||||
:on-change-text #(re-frame/dispatch [:set-in [:add-account :account :name] %])}]
|
||||
[react/text {:style {:margin-top 30}} (i18n/label :t/account-color)]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(re-frame/dispatch [:show-popover
|
||||
{:view [colors-popover (:color account)
|
||||
(fn [new-color]
|
||||
(re-frame/dispatch [:set-in [:add-account :account :color] new-color])
|
||||
(re-frame/dispatch [:hide-popover]))]
|
||||
:style {:max-height "60%"}}])}
|
||||
[react/view {:height 52 :margin-top 12 :background-color (:color account) :border-radius 8
|
||||
:align-items :flex-end :justify-content :center :padding-right 12}
|
||||
[icons/icon :main-icons/dropdown {:color colors/white}]]]]]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:right {:type :next
|
||||
:label (i18n/label :t/finish)
|
||||
:on-press #(re-frame/dispatch [:wallet.accounts/save-generated-account])
|
||||
:disabled? (string/blank? (:name account))}}]]))
|
||||
|
||||
(defn property [label value]
|
||||
[react/view {:margin-top 28}
|
||||
[react/text {:style {:color colors/gray}} label]
|
||||
|
@ -118,6 +77,7 @@
|
|||
[property (i18n/label :t/type)
|
||||
(case type
|
||||
:watch (i18n/label :t/watch-only)
|
||||
(:key :seed) (i18n/label :t/off-status-tree)
|
||||
(i18n/label :t/on-status-tree))]
|
||||
[property (i18n/label :t/wallet-address)
|
||||
[copyable-text/copyable-text-view
|
||||
|
|
|
@ -15,25 +15,29 @@
|
|||
:title :t/wallet-manage-assets
|
||||
:icon :main-icons/token
|
||||
:accessibility-label :wallet-manage-assets
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :wallet-settings-assets])}]
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:navigate-to :wallet-settings-assets])}]
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/set-currency
|
||||
:icon :main-icons/language
|
||||
:accessibility-label :wallet-set-currency
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :currency-settings])}]
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:navigate-to :currency-settings])}]
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/view-signing
|
||||
:icon :main-icons/info
|
||||
:on-press #(hide-sheet-and-dispatch [:show-popover {:view :signing-phrase}])}]
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:show-popover {:view :signing-phrase}])}]
|
||||
(when mnemonic
|
||||
[list-item/list-item
|
||||
{:theme :action-destructive
|
||||
:title :t/wallet-backup-recovery-title
|
||||
:icon :main-icons/security
|
||||
:accessibility-label :wallet-backup-recovery-title
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :backup-seed])}])]))
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:navigate-to :backup-seed])}])]))
|
||||
|
||||
(defn send-receive [account type]
|
||||
[react/view
|
||||
|
@ -43,7 +47,8 @@
|
|||
:title :t/wallet-send
|
||||
:icon :main-icons/send
|
||||
:accessibility-label :send-transaction-button
|
||||
:on-press #(hide-sheet-and-dispatch [:wallet/prepare-transaction-from-wallet account])}])
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:wallet/prepare-transaction-from-wallet account])}])
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/receive
|
||||
|
@ -56,27 +61,40 @@
|
|||
(defn add-account []
|
||||
[react/view
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/add-an-account
|
||||
{:title :t/generate-a-new-account
|
||||
:theme :action
|
||||
:icon :main-icons/add
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :add-new-account])}]
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:wallet.accounts/start-adding-new-account
|
||||
{:type :generate}])}]
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/add-a-watch-account
|
||||
:icon :main-icons/watch
|
||||
:on-press #(hide-sheet-and-dispatch [:wallet.accounts/start-adding-new-account {:type :watch}])}]])
|
||||
{:theme :action
|
||||
:title :t/add-a-watch-account
|
||||
:icon :main-icons/watch
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:wallet.accounts/start-adding-new-account
|
||||
{:type :watch}])}]
|
||||
[list-item/list-item
|
||||
{:title :t/enter-a-seed-phrase
|
||||
:theme :action
|
||||
:icon :main-icons/text
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:wallet.accounts/start-adding-new-account
|
||||
{:type :seed}])}]
|
||||
[list-item/list-item
|
||||
{:title :t/enter-a-private-key
|
||||
:theme :action
|
||||
:icon :main-icons/address
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:wallet.accounts/start-adding-new-account
|
||||
{:type :key}])}]])
|
||||
|
||||
(defn account-settings []
|
||||
[react/view
|
||||
[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/account-settings
|
||||
{:theme :action
|
||||
:title :t/account-settings
|
||||
:accessibility-label :account-settings-bottom-sheet
|
||||
:icon :main-icons/info
|
||||
:on-press #(hide-sheet-and-dispatch [:navigate-to :account-settings])}]
|
||||
;; Commented out for v1
|
||||
#_[list-item/list-item
|
||||
{:theme :action
|
||||
:title :t/export-account
|
||||
:icon :main-icons/copy
|
||||
:disabled? true}]])
|
||||
:icon :main-icons/info
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:navigate-to :account-settings])}]])
|
|
@ -45,7 +45,7 @@
|
|||
(defn add-card []
|
||||
[react/touchable-highlight {:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content sheets/add-account
|
||||
:content-height 130}])}
|
||||
:content-height 260}])}
|
||||
[react/view {:style styles/add-card}
|
||||
[react/view {:width 40 :height 40 :justify-content :center :border-radius 20
|
||||
:align-items :center :background-color colors/blue-transparent-10 :margin-bottom 8}
|
||||
|
|
|
@ -5,62 +5,19 @@
|
|||
[status-im.i18n :as i18n]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.list-item.views :as list-item]
|
||||
[reagent.core :as reagent]
|
||||
[cljs.spec.alpha :as spec]
|
||||
[status-im.multiaccounts.db :as multiaccounts.db]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.styles :as components.styles]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.utils.utils :as utils.utils]))
|
||||
|
||||
(defn add-account []
|
||||
[react/view {:flex 1}
|
||||
[topbar/topbar]
|
||||
[react/scroll-view {:keyboard-should-persist-taps :handled
|
||||
:style {:flex 1}}
|
||||
[react/view {:align-items :center :padding-horizontal 40 :margin-bottom 52}
|
||||
[react/text {:style {:typography :header :margin-top 16}}
|
||||
(i18n/label :t/add-an-account)]
|
||||
[react/text {:style {:color colors/gray :text-align :center :margin-top 16 :line-height 22}}
|
||||
(i18n/label :t/add-account-description)]]
|
||||
[list-item/list-item
|
||||
{:type :section-header
|
||||
:title :t/default}]
|
||||
[list-item/list-item
|
||||
{:title :t/generate-a-new-account
|
||||
:theme :action
|
||||
:icon :main-icons/add
|
||||
:accessories [:chevron]
|
||||
:on-press #(re-frame/dispatch [:wallet.accounts/start-adding-new-account {:type :generate}])}]
|
||||
;;TODO: implement adding account by seedphrase and private key
|
||||
#_[list-item/list-item
|
||||
{:type :section-header
|
||||
:container-margin-top 24
|
||||
:title (i18n/label :t/advanced)}]
|
||||
#_[list-item/list-item
|
||||
{:title (i18n/label :t/enter-a-seed-phrase)
|
||||
:theme :action
|
||||
:icon :main-icons/add
|
||||
:accessories [:chevron]
|
||||
:disabled? true
|
||||
:on-press #(re-frame/dispatch [:wallet.accounts/start-adding-new-account {:type :seed}])}]
|
||||
#_[list-item/list-item
|
||||
{:title (i18n/label :t/enter-a-private-key)
|
||||
:theme :action
|
||||
:icon :main-icons/add
|
||||
:accessories [:chevron]
|
||||
:disabled? true
|
||||
:on-press #(re-frame/dispatch [:wallet.accounts/start-adding-new-account {:type :key}])}]]])
|
||||
|
||||
(def input-container
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:border-radius components.styles/border-radius
|
||||
:height 52
|
||||
:margin 16
|
||||
:padding-horizontal 16
|
||||
:background-color colors/gray-lighter})
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[status-im.ui.components.text-input.view :as text-input]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[status-im.ui.screens.wallet.account-settings.views :as account-settings]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.utils.security :as security]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(defn- request-camera-permissions []
|
||||
(let [options {:handler :wallet.add-new/qr-scanner-result}]
|
||||
|
@ -76,37 +33,119 @@
|
|||
(i18n/label :t/camera-access-error)))
|
||||
50)}])))
|
||||
|
||||
(defview add-watch-account []
|
||||
(letsubs [add-account-disabled? [:add-account-disabled?]
|
||||
add-account-scanned-address [:add-account-scanned-address]]
|
||||
[react/keyboard-avoiding-view {:flex 1}
|
||||
[topbar/topbar {:accessories [{:icon :qr :handler #(request-camera-permissions)}]}]
|
||||
[react/view {:flex 1
|
||||
:justify-content :space-between
|
||||
:align-items :center :margin-horizontal 16}
|
||||
[react/view
|
||||
[react/text {:style {:typography :header :margin-top 16}}
|
||||
(i18n/label :t/add-a-watch-account)]
|
||||
[react/text {:style {:color colors/gray :text-align :center :margin-vertical 16}}
|
||||
(i18n/label :t/enter-watch-account-address)]]
|
||||
[react/view {:align-items :center :flex 1 :flex-direction :row}
|
||||
[react/text-input {:auto-focus true
|
||||
:multiline true
|
||||
:text-align :center
|
||||
:default-value add-account-scanned-address
|
||||
:placeholder (i18n/label :t/enter-address)
|
||||
:style {:typography :header :flex 1}
|
||||
:on-change-text #(re-frame/dispatch [:set-in [:add-account :address] %])}]]]
|
||||
(defn add-account-topbar [type]
|
||||
(let [title (case type
|
||||
:generate :t/generate-an-account
|
||||
:watch :t/add-watch-account
|
||||
:seed :t/add-seed-account
|
||||
:key :t/add-private-key-account
|
||||
"")]
|
||||
[topbar/topbar
|
||||
(merge {:title title}
|
||||
(when (= type :watch)
|
||||
{:accessories [{:icon :qr
|
||||
:handler #(request-camera-permissions)}]}))]))
|
||||
|
||||
(defn common-settings [account]
|
||||
[react/view {:margin-horizontal 16 :margin-top 30}
|
||||
[text-input/text-input-with-label
|
||||
{:label (i18n/label :t/account-name)
|
||||
:auto-focus false
|
||||
:default-value (:name account)
|
||||
:placeholder (i18n/label :t/account-name)
|
||||
:on-change-text #(re-frame/dispatch [:set-in [:add-account :account :name] %])}]
|
||||
[react/text {:style {:margin-top 30}} (i18n/label :t/account-color)]
|
||||
[react/touchable-highlight
|
||||
{:on-press #(re-frame/dispatch
|
||||
[:show-popover
|
||||
{:view [account-settings/colors-popover (:color account)
|
||||
(fn [new-color]
|
||||
(re-frame/dispatch [:set-in [:add-account :account :color] new-color])
|
||||
(re-frame/dispatch [:hide-popover]))]
|
||||
:style {:max-height "60%"}}])}
|
||||
[react/view {:height 52 :margin-top 12 :background-color (:color account) :border-radius 8
|
||||
:align-items :flex-end :justify-content :center :padding-right 12}
|
||||
[icons/icon :main-icons/dropdown {:color colors/white}]]]])
|
||||
|
||||
(defn settings [{:keys [type scanned-address password-error account-error]}
|
||||
entered-password]
|
||||
[react/view {:margin-horizontal 16}
|
||||
(if (= type :watch)
|
||||
[text-input/text-input-with-label
|
||||
{:label (i18n/label :t/wallet-key-title)
|
||||
:auto-focus false
|
||||
:default-value scanned-address
|
||||
:placeholder (i18n/label :t/enter-address)
|
||||
:on-change-text #(re-frame/dispatch [:set-in [:add-account :address] %])}]
|
||||
[text-input/text-input-with-label
|
||||
{:label (i18n/label :t/password)
|
||||
:parent-container {:margin-top 30}
|
||||
:auto-focus false
|
||||
:placeholder (i18n/label :t/enter-your-password)
|
||||
:secure-text-entry true
|
||||
:text-content-type :none
|
||||
:error (when password-error (i18n/label :t/add-account-incorrect-password))
|
||||
:on-change-text #(do
|
||||
(re-frame/dispatch [:set-in [:add-account :password-error] nil])
|
||||
(reset! entered-password %))}])
|
||||
(when (= type :seed)
|
||||
[text-input/text-input-with-label
|
||||
{:parent-container {:margin-top 30}
|
||||
:label (i18n/label :t/recovery-phrase)
|
||||
:auto-focus false
|
||||
:placeholder (i18n/label :t/multiaccounts-recover-enter-phrase-title)
|
||||
:auto-correct false
|
||||
:keyboard-type "visible-password"
|
||||
:multiline true
|
||||
:style (when platform/android?
|
||||
{:flex 1})
|
||||
:height 95
|
||||
:error account-error
|
||||
:on-change-text
|
||||
#(do
|
||||
(re-frame/dispatch [:set-in [:add-account :account-error] nil])
|
||||
(re-frame/dispatch [:set-in [:add-account :seed] (security/mask-data (string/lower-case %))]))}])
|
||||
(when (= type :key)
|
||||
[text-input/text-input-with-label
|
||||
{:parent-container {:margin-top 30}
|
||||
:label (i18n/label :t/private-key)
|
||||
:auto-focus false
|
||||
:placeholder (i18n/label :t/enter-a-private-key)
|
||||
:auto-correct false
|
||||
:keyboard-type "visible-password"
|
||||
:error account-error
|
||||
:secure-text-entry true
|
||||
:text-content-type :none
|
||||
:on-change-text
|
||||
#(do
|
||||
(re-frame/dispatch [:set-in [:add-account :account-error] nil])
|
||||
(re-frame/dispatch [:set-in [:add-account :private-key] (security/mask-data %)]))}])])
|
||||
|
||||
(defview add-account []
|
||||
(letsubs [{:keys [type account] :as add-account} [:add-account]
|
||||
add-account-disabled? [:add-account-disabled?]
|
||||
entered-password (reagent/atom "")]
|
||||
[react/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[add-account-topbar type]
|
||||
[react/scroll-view {:keyboard-should-persist-taps :handled
|
||||
:style {:flex 1}}
|
||||
[settings add-account entered-password]
|
||||
[common-settings account]]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:right {:type :next
|
||||
:label (i18n/label :t/next)
|
||||
:on-press #(re-frame/dispatch [:wallet.accounts/add-watch-account])
|
||||
:disabled? add-account-disabled?}}]]))
|
||||
:right
|
||||
{:type :next
|
||||
:label :t/add-account
|
||||
:on-press #(re-frame/dispatch [:wallet.accounts/add-new-account
|
||||
(ethereum/sha3 @entered-password)])
|
||||
:disabled? (or add-account-disabled?
|
||||
(and
|
||||
(not (= type :watch))
|
||||
(not (spec/valid? ::multiaccounts.db/password @entered-password))))}}]]))
|
||||
|
||||
(defview pin []
|
||||
(letsubs [pin [:hardwallet/pin]
|
||||
status [:hardwallet/pin-status]
|
||||
(letsubs [pin [:hardwallet/pin]
|
||||
status [:hardwallet/pin-status]
|
||||
error-label [:hardwallet/pin-error-label]]
|
||||
[react/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[topbar/topbar]
|
||||
|
@ -116,32 +155,4 @@
|
|||
:title-label :t/current-pin
|
||||
:description-label :t/current-pin-description
|
||||
:error-label error-label
|
||||
:step :export-key}]]))
|
||||
|
||||
(defview password []
|
||||
(letsubs [{:keys [error]} [:add-account]
|
||||
entered-password (reagent/atom "")]
|
||||
[react/keyboard-avoiding-view {:style {:flex 1}}
|
||||
[topbar/topbar]
|
||||
[react/view {:flex 1
|
||||
:justify-content :space-between
|
||||
:align-items :center :margin-horizontal 16}
|
||||
[react/text {:style {:typography :header :margin-top 16}} (i18n/label :t/enter-your-password)]
|
||||
[react/view {:justify-content :center :flex 1}
|
||||
[react/text-input {:secure-text-entry true
|
||||
:auto-focus true
|
||||
:auto-capitalize :none
|
||||
:text-align :center
|
||||
:placeholder ""
|
||||
:style {:typography :header}
|
||||
:on-change-text #(reset! entered-password %)}]
|
||||
(when error
|
||||
[react/text {:style {:text-align :center :color colors/red :margin-top 76}} error])]
|
||||
[react/text {:style {:color colors/gray :text-align :center :margin-bottom 16}}
|
||||
(i18n/label :t/to-encrypt-enter-password)]]
|
||||
[toolbar/toolbar
|
||||
{:show-border? true
|
||||
:right {:type :next
|
||||
:label :t/generate-account
|
||||
:on-press #(re-frame/dispatch [:wallet.accounts/generate-new-account @entered-password])
|
||||
:disabled? (not (spec/valid? ::multiaccounts.db/password @entered-password))}}]]))
|
||||
:step :export-key}]]))
|
|
@ -13,78 +13,228 @@
|
|||
[status-im.ui.screens.navigation :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.wallet.core :as wallet]))
|
||||
[status-im.wallet.core :as wallet]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.security :as security]
|
||||
[status-im.multiaccounts.recover.core :as recover]
|
||||
[status-im.ethereum.mnemonic :as mnemonic]))
|
||||
|
||||
(fx/defn start-adding-new-account
|
||||
{:events [:wallet.accounts/start-adding-new-account]}
|
||||
[{:keys [db] :as cofx} {:keys [type] :as add-account}]
|
||||
(let [{:keys [latest-derived-path]} (:multiaccount db)
|
||||
path-num (inc latest-derived-path)
|
||||
account (merge
|
||||
{:color (rand-nth colors/account-colors)}
|
||||
(when (= type :generate)
|
||||
{:name (str "Account " path-num)}))]
|
||||
(fx/merge cofx
|
||||
{:db (assoc db :add-account (assoc add-account :account account))}
|
||||
(navigation/navigate-to-cofx :add-new-account nil))))
|
||||
|
||||
(fx/defn new-account-error
|
||||
{:events [::new-account-error]}
|
||||
[{:keys [db]} error-key error]
|
||||
{:db (update db :add-account merge {error-key error
|
||||
:step nil})})
|
||||
|
||||
(defn account-stored [path type]
|
||||
(fn [result]
|
||||
(let [{:keys [error publicKey address]} (types/json->clj result)]
|
||||
(if error
|
||||
(re-frame/dispatch [::new-account-error :account-error error])
|
||||
(re-frame/dispatch [:wallet.accounts/account-stored
|
||||
{:address address
|
||||
:public-key publicKey
|
||||
:type type
|
||||
:path path}])))))
|
||||
|
||||
(def dec-pass-error "could not decrypt key with given password")
|
||||
|
||||
(defn normalize-path [path]
|
||||
(if (string/starts-with? path "m/")
|
||||
(str constants/path-wallet-root
|
||||
"/" (last (string/split path "/")))
|
||||
path))
|
||||
|
||||
(defn derive-and-store-account [path hashed-password type]
|
||||
(fn [value]
|
||||
(let [{:keys [id error]} (types/json->clj value)]
|
||||
(if error
|
||||
(re-frame/dispatch [::new-account-error :password-error error])
|
||||
(status/multiaccount-derive-addresses
|
||||
id
|
||||
[path]
|
||||
(fn [_]
|
||||
(status/multiaccount-store-derived
|
||||
id
|
||||
[path]
|
||||
hashed-password
|
||||
(fn [result]
|
||||
(let [{:keys [error] :as result} (types/json->clj result)
|
||||
{:keys [publicKey address]} (get result (keyword path))]
|
||||
(if error
|
||||
(re-frame/dispatch [::new-account-error :account-error error])
|
||||
(re-frame/dispatch
|
||||
[:wallet.accounts/account-stored
|
||||
{:address address
|
||||
:public-key publicKey
|
||||
:type type
|
||||
:path (normalize-path path)}])))))))))))
|
||||
|
||||
(def pass-error "cannot retrieve a valid key for a given account: could not decrypt key with given password")
|
||||
|
||||
(defn store-account [path hashed-password type]
|
||||
(fn [value]
|
||||
(let [{:keys [id error]} (types/json->clj value)]
|
||||
(if error
|
||||
(re-frame/dispatch [::new-account-error
|
||||
(if (= error pass-error) :password-error :account-error)
|
||||
error])
|
||||
(status/multiaccount-store-account
|
||||
id
|
||||
hashed-password
|
||||
(account-stored path type))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:list.selection/open-share
|
||||
(fn [obj]
|
||||
(list-selection/open-share obj)))
|
||||
::verify-password
|
||||
(fn [{:keys [address hashed-password]}]
|
||||
(status/verify
|
||||
address hashed-password
|
||||
#(re-frame/dispatch [:wallet.accounts/add-new-account-password-verifyied % hashed-password]))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::generate-account
|
||||
(fn [{:keys [derivation-info hashed-password path-num]}]
|
||||
(fn [{:keys [derivation-info hashed-password]}]
|
||||
(let [{:keys [address path]} derivation-info]
|
||||
(status/multiaccount-load-account
|
||||
address
|
||||
hashed-password
|
||||
(fn [value]
|
||||
(let [{:keys [id error]} (types/json->clj value)]
|
||||
(if error
|
||||
(re-frame/dispatch [::generate-new-account-error])
|
||||
(status/multiaccount-derive-addresses
|
||||
id
|
||||
[path]
|
||||
(fn [result]
|
||||
(status/multiaccount-store-derived
|
||||
id
|
||||
[path]
|
||||
hashed-password
|
||||
(fn [result]
|
||||
(let [{:keys [publicKey address]}
|
||||
(get (types/json->clj result) (keyword path))]
|
||||
(re-frame/dispatch [:wallet.accounts/account-generated
|
||||
{:name (str "Account " path-num)
|
||||
:address address
|
||||
:public-key publicKey
|
||||
:path (str constants/path-wallet-root "/" path-num)
|
||||
:color (rand-nth colors/account-colors)}])))))))))))))
|
||||
(derive-and-store-account path hashed-password :generated)))))
|
||||
|
||||
(fx/defn set-symbol-request
|
||||
{:events [:wallet.accounts/share]}
|
||||
[_ address]
|
||||
{:list.selection/open-share {:message (eip55/address->checksum address)}})
|
||||
(re-frame/reg-fx
|
||||
::import-account-seed
|
||||
(fn [{:keys [passphrase hashed-password]}]
|
||||
(status/multiaccount-import-mnemonic
|
||||
(mnemonic/sanitize-passphrase (security/unmask passphrase))
|
||||
""
|
||||
(derive-and-store-account constants/path-default-wallet hashed-password :seed))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::import-account-private-key
|
||||
(fn [{:keys [private-key hashed-password]}]
|
||||
(status/multiaccount-import-private-key
|
||||
(string/trim (security/unmask private-key))
|
||||
(store-account constants/path-default-wallet hashed-password :key))))
|
||||
|
||||
(fx/defn generate-new-account
|
||||
{:events [:wallet.accounts/generate-new-account]}
|
||||
[{:keys [db]} password]
|
||||
[{:keys [db]} hashed-password]
|
||||
(let [wallet-root-address (get-in db [:multiaccount :wallet-root-address])
|
||||
path-num (inc (get-in db [:multiaccount :latest-derived-path]))]
|
||||
(when-not (get-in db [:add-account :step])
|
||||
{:db (assoc-in db [:add-account :step] :generating)
|
||||
::generate-account {:derivation-info (if wallet-root-address
|
||||
;; Use the walllet-root-address for stored on disk keys
|
||||
;; This needs to be the RELATIVE path to the key used to derive
|
||||
{:path (str "m/" path-num)
|
||||
:address wallet-root-address}
|
||||
;; Fallback on the master account for keycards, use the absolute path
|
||||
{:path (str constants/path-wallet-root "/" path-num)
|
||||
:address (get-in db [:multiaccount :address])})
|
||||
:path-num path-num
|
||||
:hashed-password (ethereum/sha3 password)}})))
|
||||
{:db (assoc-in db [:add-account :step] :generating)
|
||||
::generate-account {:derivation-info (if wallet-root-address
|
||||
;; Use the walllet-root-address for stored on disk keys
|
||||
;; This needs to be the RELATIVE path to the key used to derive
|
||||
{:path (str "m/" path-num)
|
||||
:address wallet-root-address}
|
||||
;; Fallback on the master account for keycards, use the absolute path
|
||||
{:path (str constants/path-wallet-root "/" path-num)
|
||||
:address (get-in db [:multiaccount :address])})
|
||||
:hashed-password hashed-password}}))
|
||||
|
||||
(fx/defn generate-new-account-error
|
||||
{:events [::generate-new-account-error]}
|
||||
[{:keys [db]} password]
|
||||
{:db (assoc db
|
||||
:add-account
|
||||
{:error (i18n/label :t/add-account-incorrect-password)})})
|
||||
(fx/defn import-new-account-seed
|
||||
[{:keys [db]} passphrase hashed-password]
|
||||
{:db (assoc-in db [:add-account :step] :generating)
|
||||
::recover/validate-mnemonic [(security/safe-unmask-data passphrase)
|
||||
#(re-frame/dispatch [:wallet.accounts/seed-validated
|
||||
% passphrase hashed-password])]})
|
||||
|
||||
(fx/defn new-account-seed-validated
|
||||
{:events [:wallet.accounts/seed-validated]}
|
||||
[cofx phrase-warnings passphrase hashed-password]
|
||||
(let [error (:error (types/json->clj phrase-warnings))]
|
||||
(if-not (string/blank? error)
|
||||
(new-account-error cofx :account-error error)
|
||||
{::import-account-seed {:passphrase passphrase
|
||||
:hashed-password hashed-password}})))
|
||||
|
||||
(fx/defn import-new-account-private-key
|
||||
[{:keys [db]} private-key hashed-password]
|
||||
{:db (assoc-in db [:add-account :step] :generating)
|
||||
::import-account-private-key {:private-key private-key
|
||||
:hashed-password hashed-password}})
|
||||
|
||||
(fx/defn save-new-account
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [latest-derived-path]} (:multiaccount db)
|
||||
{:keys [account type]} (:add-account db)
|
||||
accounts (:multiaccount/accounts db)
|
||||
new-accounts (conj accounts account)]
|
||||
(when account
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "accounts_saveAccounts"
|
||||
:params [[account]]
|
||||
:on-success #()}]
|
||||
:db (-> db
|
||||
(assoc :multiaccount/accounts new-accounts)
|
||||
(dissoc :add-account))}
|
||||
(when (= type :generate)
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:latest-derived-path (inc latest-derived-path)
|
||||
{}))))))
|
||||
|
||||
(fx/defn account-generated
|
||||
{:events [:wallet.accounts/account-generated]}
|
||||
[{:keys [db] :as cofx} account]
|
||||
(fx/merge cofx
|
||||
{:db (update db :add-account assoc :account account :step :generated)}
|
||||
(navigation/navigate-to-cofx :account-added nil)))
|
||||
{:events [:wallet.accounts/account-stored]}
|
||||
[{:keys [db] :as cofx} {:keys [address] :as account}]
|
||||
(let [accounts (:multiaccount/accounts db)]
|
||||
(if (some #(when (= (:address %) address) %) accounts)
|
||||
(new-account-error cofx :account-error (i18n/label :t/account-exists-title))
|
||||
(fx/merge cofx
|
||||
{:db (update-in db [:add-account :account] merge account)}
|
||||
(save-new-account)
|
||||
(wallet/update-balances nil)
|
||||
(wallet/update-prices)
|
||||
(navigation/navigate-back)))))
|
||||
|
||||
(fx/defn add-watch-account
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [address (get-in db [:add-account :address])]
|
||||
(account-generated cofx {:address (eip55/address->checksum (ethereum/normalized-hex address))
|
||||
:type :watch})))
|
||||
|
||||
(fx/defn add-new-account-password-verifyied
|
||||
{:events [:wallet.accounts/add-new-account-password-verifyied]}
|
||||
[{:keys [db] :as cofx} result hashed-password]
|
||||
(let [{:keys [error]} (types/json->clj result)]
|
||||
(if (not (string/blank? error))
|
||||
(new-account-error cofx :password-error error)
|
||||
(let [{:keys [type step seed private-key]} (:add-account db)]
|
||||
(case type
|
||||
:seed
|
||||
(import-new-account-seed cofx seed hashed-password)
|
||||
:key
|
||||
(import-new-account-private-key cofx private-key hashed-password)
|
||||
nil)))))
|
||||
|
||||
(fx/defn add-new-account-verify-password
|
||||
[{:keys [db]} hashed-password]
|
||||
{:db (assoc-in db [:add-account :step] :generating)
|
||||
::verify-password {:address (get-in db [:multiaccount :wallet-root-address])
|
||||
:hashed-password hashed-password}})
|
||||
|
||||
(fx/defn add-new-account
|
||||
{:events [:wallet.accounts/add-new-account]}
|
||||
[{:keys [db] :as cofx} hashed-password]
|
||||
(let [{:keys [type step]} (:add-account db)]
|
||||
(when-not step
|
||||
(case type
|
||||
:watch
|
||||
(add-watch-account cofx)
|
||||
:generate
|
||||
(generate-new-account cofx hashed-password)
|
||||
(:seed :key)
|
||||
(add-new-account-verify-password cofx hashed-password)
|
||||
nil))))
|
||||
|
||||
(fx/defn save-account
|
||||
{:events [:wallet.accounts/save-account]}
|
||||
|
@ -114,63 +264,6 @@
|
|||
(assoc-in [:wallet :accounts deleted-address] nil))}
|
||||
(navigation/navigate-to-cofx :wallet nil))))
|
||||
|
||||
(fx/defn save-generated-account
|
||||
{:events [:wallet.accounts/save-generated-account]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [latest-derived-path]} (:multiaccount db)
|
||||
{:keys [account path type]} (:add-account db)
|
||||
accounts (:multiaccount/accounts db)
|
||||
new-accounts (conj accounts account)]
|
||||
(when account
|
||||
(fx/merge cofx
|
||||
{::json-rpc/call [{:method "accounts_saveAccounts"
|
||||
:params [[account]]
|
||||
:on-success #()}]
|
||||
:db (-> db
|
||||
(assoc :multiaccount/accounts new-accounts)
|
||||
(dissoc :add-account))}
|
||||
(when (= type :generate)
|
||||
(multiaccounts.update/multiaccount-update
|
||||
:latest-derived-path (inc latest-derived-path)
|
||||
{}))
|
||||
(wallet/update-balances nil)
|
||||
(navigation/navigate-to-cofx :wallet nil)))))
|
||||
|
||||
(fx/defn start-adding-new-account
|
||||
{:events [:wallet.accounts/start-adding-new-account]}
|
||||
[{:keys [db] :as cofx} {:keys [type] :as add-account}]
|
||||
(let [{:keys [keycard-pairing]} (:multiaccount db)
|
||||
screen (case type
|
||||
:generate (if keycard-pairing :add-new-account-pin
|
||||
:add-new-account-password)
|
||||
:watch :add-watch-account)]
|
||||
(fx/merge cofx
|
||||
{:db (cond-> (assoc db :add-account add-account)
|
||||
keycard-pairing
|
||||
(assoc-in [:hardwallet :pin :enter-step] :export-key))}
|
||||
(navigation/navigate-to-cofx screen nil))))
|
||||
|
||||
(fx/defn enter-phrase-next-pressed
|
||||
{:events [:wallet.accounts/enter-phrase-next-pressed]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(dissoc :intro-wizard)
|
||||
(assoc-in [:add-account :seed] (get-in db [:intro-wizard :passphrase])))}
|
||||
(navigation/navigate-to-cofx :add-new-account-password nil)))
|
||||
|
||||
(fx/defn add-watch-account
|
||||
{:events [:wallet.accounts/add-watch-account]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [address (get-in db [:add-account :address])]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:add-account :account]
|
||||
{:name ""
|
||||
:address (eip55/address->checksum (ethereum/normalized-hex address))
|
||||
:type :watch
|
||||
:color (rand-nth colors/account-colors)})}
|
||||
(navigation/navigate-to-cofx :account-added nil))))
|
||||
|
||||
(fx/defn view-only-qr-scanner-result
|
||||
{:events [:wallet.add-new/qr-scanner-result]}
|
||||
[{db :db :as cofx} data _]
|
||||
|
@ -184,3 +277,13 @@
|
|||
{:utils/show-popup {:title (i18n/label :t/error)
|
||||
:content (i18n/label :t/invalid-address-qr-code)}}))
|
||||
(navigation/navigate-back))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:list.selection/open-share
|
||||
(fn [obj]
|
||||
(list-selection/open-share obj)))
|
||||
|
||||
(fx/defn wallet-accounts-share
|
||||
{:events [:wallet.accounts/share]}
|
||||
[_ address]
|
||||
{:list.selection/open-share {:message (eip55/address->checksum address)}})
|
|
@ -443,7 +443,7 @@
|
|||
"gas-price": "Gas price",
|
||||
"gas-used": "Gas used",
|
||||
"generate-a-key": "Generate keys",
|
||||
"generate-a-new-account": "Generate keys",
|
||||
"generate-a-new-account": "Generate an account",
|
||||
"generate-a-new-key": "Generate a new key",
|
||||
"generate-account": "Generate keys",
|
||||
"generate-new-key": "Generate keys",
|
||||
|
@ -1079,6 +1079,7 @@
|
|||
"select-account-dapp": "Select the account you wish to use with Dapps",
|
||||
"apply": "Apply",
|
||||
"on-status-tree": "On Status tree",
|
||||
"off-status-tree": "Off Status tree",
|
||||
"derivation-path": "Derivation path",
|
||||
"storage": "Storage",
|
||||
"keycard-free-pairing-slots": "Keycard has {{n}} free pairing slots",
|
||||
|
@ -1089,5 +1090,11 @@
|
|||
"mail-should-be-configured": "Mail client should be configured",
|
||||
"check-on-etherscan": "Check on etherscan",
|
||||
"transactions-load-more": "Load more",
|
||||
"private-key": "Private key",
|
||||
"generate-an-account": "Generate an account",
|
||||
"add-watch-account": "Add a watch-only account",
|
||||
"add-seed-account": "Add account with a seed phrase",
|
||||
"account-exists-title": "Account already exists",
|
||||
"add-private-key-account": "Add account from private key",
|
||||
"user-not-found": "User not found"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue