From ba112a765bda13770064e11a95b340c135954dbc Mon Sep 17 00:00:00 2001 From: Andrey Shovkoplyas Date: Mon, 12 Aug 2019 10:53:31 +0200 Subject: [PATCH] [#8666] Add account via BIP 44 Signed-off-by: Andrey Shovkoplyas --- .../src/status_im/ui/components/react.cljs | 2 + externs.js | 7 +- .../status/ethereum/module/StatusModule.java | 76 +++++++ .../ios/RCTStatus/RCTStatus.m | 38 ++++ .../chat/commands/impl/transactions.cljs | 7 +- src/status_im/constants.cljs | 1 + .../realm/schemas/base/account.cljs | 1 + .../data_store/realm/schemas/base/core.cljs | 8 + src/status_im/ethereum/json_rpc.cljs | 1 + src/status_im/ethereum/subscriptions.cljs | 10 +- src/status_im/ethereum/transactions/core.cljs | 53 ++--- src/status_im/events.cljs | 24 +-- src/status_im/multiaccounts/create/core.cljs | 13 +- src/status_im/multiaccounts/login/core.cljs | 2 +- src/status_im/native_module/core.cljs | 12 ++ src/status_im/native_module/impl/module.cljs | 26 +++ src/status_im/signing/core.cljs | 37 ++-- src/status_im/subs.cljs | 192 +++++++++++------- src/status_im/ui/components/colors.cljs | 8 + .../ui/components/list_item/views.cljs | 17 +- .../ui/components/status_bar/view.cljs | 1 + src/status_im/ui/screens/db.cljs | 3 +- src/status_im/ui/screens/popover/views.cljs | 17 +- src/status_im/ui/screens/routing/screens.cljs | 11 +- .../ui/screens/routing/wallet_stack.cljs | 6 +- src/status_im/ui/screens/signing/views.cljs | 14 +- src/status_im/ui/screens/stickers/views.cljs | 2 +- .../ui/screens/wallet/account/styles.cljs | 7 +- .../ui/screens/wallet/account/views.cljs | 41 ++-- .../ui/screens/wallet/accounts/sheets.cljs | 8 +- .../ui/screens/wallet/accounts/styles.cljs | 2 +- .../ui/screens/wallet/accounts/views.cljs | 4 +- .../ui/screens/wallet/add_new/views.cljs | 127 ++++++++++++ .../ui/screens/wallet/components/views.cljs | 47 ++++- .../screens/wallet/custom_tokens/views.cljs | 12 +- .../ui/screens/wallet/navigation.cljs | 4 + .../ui/screens/wallet/request/views.cljs | 2 +- .../ui/screens/wallet/send/views.cljs | 3 +- .../screens/wallet/transactions/styles.cljs | 1 + .../ui/screens/wallet/transactions/views.cljs | 43 ++-- src/status_im/wallet/accounts/core.cljs | 59 +++++- src/status_im/wallet/core.cljs | 113 +++++------ src/status_im/wallet/custom_tokens/core.cljs | 3 +- src/status_im/wallet/db.cljs | 3 +- test/cljs/status_im/test/signing/core.cljs | 6 +- translations/en.json | 14 +- 46 files changed, 757 insertions(+), 331 deletions(-) create mode 100644 src/status_im/ui/screens/wallet/add_new/views.cljs diff --git a/components/src/status_im/ui/components/react.cljs b/components/src/status_im/ui/components/react.cljs index f9a5543d11..bb9751bda5 100644 --- a/components/src/status_im/ui/components/react.cljs +++ b/components/src/status_im/ui/components/react.cljs @@ -306,6 +306,7 @@ :wallet-request-assets :choose-recipient :recent-recipients + :select-account :wallet-send-transaction-request :contact-code :wallet-settings-hook) @@ -317,6 +318,7 @@ colors/white)}) bottom-background (when (#{:recent-recipients + :select-account :wallet-send-assets :wallet-request-assets} current-view) [view {:background-color colors/white diff --git a/externs.js b/externs.js index b17eabb778..c013b8572a 100644 --- a/externs.js +++ b/externs.js @@ -244,6 +244,7 @@ var TopLevel = { "messaging" : function () {}, "method" : function () {}, "minus" : function () {}, + "plus" : function () {}, "mkdir" : function () {}, "module" : function () {}, "moveFile" : function () {}, @@ -563,5 +564,9 @@ var TopLevel = { "createAppContainer" : function () {}, "useScreens" : function () {}, "multiAccountGenerateAndDeriveAddresses" : function () {}, - "multiAccountStoreDerived" : function () {} + "multiAccountStoreDerived" : function () {}, + "multiAccountDeriveAddresses" : function () {}, + "multiAccountReset" : function () {}, + "multiAccountLoadAccount" : function () {}, + "multiAccountStoreAccount" : function () {} } diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index 7777a8d835..81db30c3c1 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -660,6 +660,82 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL StatusThreadPoolExecutor.getInstance().execute(r); } + @ReactMethod + public void multiAccountStoreAccount(final String json, final Callback callback) { + Log.d(TAG, "multiAccountStoreAccount"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.multiAccountStoreAccount(json); + + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + + @ReactMethod + public void multiAccountLoadAccount(final String json, final Callback callback) { + Log.d(TAG, "multiAccountLoadAccount"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.multiAccountLoadAccount(json); + + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + + @ReactMethod + public void multiAccountReset(final Callback callback) { + Log.d(TAG, "multiAccountReset"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.multiAccountReset(); + + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + + @ReactMethod + public void multiAccountDeriveAddresses(final String json, final Callback callback) { + Log.d(TAG, "multiAccountDeriveAddresses"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.multiAccountDeriveAddresses(json); + + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + @ReactMethod public void multiAccountGenerateAndDeriveAddresses(final String json, final Callback callback) { Log.d(TAG, "multiAccountGenerateAndDeriveAddresses"); diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index 7535501e06..00e4230df4 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -345,6 +345,35 @@ RCT_EXPORT_METHOD(multiAccountGenerateAndDeriveAddresses:(NSString *)json callback(@[result]); } +//////////////////////////////////////////////////////////////////// MultiAccountStoreAccount +RCT_EXPORT_METHOD(multiAccountStoreAccount:(NSString *)json + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"MultiAccountStoreAccount() method called"); +#endif + NSString *result = StatusgoMultiAccountStoreAccount(json); + callback(@[result]); +} + +//////////////////////////////////////////////////////////////////// MultiAccountLoadAccount +RCT_EXPORT_METHOD(multiAccountLoadAccount:(NSString *)json + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"MultiAccountLoadAccount() method called"); +#endif + NSString *result = StatusgoMultiAccountLoadAccount(json); + callback(@[result]); +} + +//////////////////////////////////////////////////////////////////// MultiAccountReset +RCT_EXPORT_METHOD(multiAccountReset:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"MultiAccountReset() method called"); +#endif + NSString *result = StatusgoMultiAccountReset(); + callback(@[result]); +} + //////////////////////////////////////////////////////////////////// multiAccountStoreDerived RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json callback:(RCTResponseSenderBlock)callback) { @@ -355,6 +384,15 @@ RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json callback(@[result]); } +//////////////////////////////////////////////////////////////////// MultiAccountDeriveAddresses +RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"MultiAccountDeriveAddresses() method called"); +#endif + NSString *result = StatusgoMultiAccountDeriveAddresses(json); + callback(@[result]); +} //////////////////////////////////////////////////////////////////// login RCT_EXPORT_METHOD(login:(NSString *)json diff --git a/src/status_im/chat/commands/impl/transactions.cljs b/src/status_im/chat/commands/impl/transactions.cljs index a0ddc964f9..442178a74f 100644 --- a/src/status_im/chat/commands/impl/transactions.cljs +++ b/src/status_im/chat/commands/impl/transactions.cljs @@ -60,8 +60,9 @@ (def assets-separator [react/view transactions-styles/asset-separator]) -(defview choose-asset [nft?] - (letsubs [assets [:wallet/visible-assets-with-amount]] +(defn choose-asset [nft?] [react/view]) +;;TODO we'll need to specify address here +#_(letsubs [assets [:wallet/visible-assets-with-amount]] [react/view [list/flat-list {:data (filter #(if nft? (:nft? %) @@ -74,7 +75,7 @@ :enableEmptySections true :separator assets-separator :keyboardShouldPersistTaps :always - :bounces false}]])) + :bounces false}]]) (defn choose-asset-suggestion [] [choose-asset false]) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 8cec65a5b8..edc0ddba94 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -237,6 +237,7 @@ (def ^:const status-create-address "status_createaddress") +(def ^:const path-root "m/44'/60'/0'/0") (def ^:const path-default-wallet "m/44'/60'/0'/0/0") (def ^:const path-whisper "m/43'/60'/1581'/0'/0") diff --git a/src/status_im/data_store/realm/schemas/base/account.cljs b/src/status_im/data_store/realm/schemas/base/account.cljs index 265a78e9d2..06d945eea0 100644 --- a/src/status_im/data_store/realm/schemas/base/account.cljs +++ b/src/status_im/data_store/realm/schemas/base/account.cljs @@ -255,3 +255,4 @@ (def v26 (update v25 :properties merge {:root-address {:type :string :optional true} :accounts {:type :string :optional true}})) +(def v27 (update v26 :properties merge {:latest-derived-path {:type :int :optional true}})) diff --git a/src/status_im/data_store/realm/schemas/base/core.cljs b/src/status_im/data_store/realm/schemas/base/core.cljs index 53d01e5f41..9289f8017a 100644 --- a/src/status_im/data_store/realm/schemas/base/core.cljs +++ b/src/status_im/data_store/realm/schemas/base/core.cljs @@ -128,6 +128,11 @@ extension/v12 account/v26]) +(def v32 [network/v1 + bootnode/v4 + extension/v12 + account/v27]) + ;; put schemas ordered by version (def schemas [{:schema v1 :schemaVersion 1 @@ -221,4 +226,7 @@ :migration (constantly nil)} {:schema v31 :schemaVersion 31 + :migration (constantly nil)} + {:schema v32 + :schemaVersion 32 :migration (constantly nil)}]) diff --git a/src/status_im/ethereum/json_rpc.cljs b/src/status_im/ethereum/json_rpc.cljs index 016c619166..ff9176d75d 100644 --- a/src/status_im/ethereum/json_rpc.cljs +++ b/src/status_im/ethereum/json_rpc.cljs @@ -42,6 +42,7 @@ "status_startOneOnOneChat" {} "status_removeChat" {} "wallet_getTransfers" {} + "wallet_getTransfersByAddress" {} "browsers_getBrowsers" {} "browsers_addBrowser" {} "browsers_deleteBrowser" {} diff --git a/src/status_im/ethereum/subscriptions.cljs b/src/status_im/ethereum/subscriptions.cljs index 4187dea17a..03c9f4689e 100644 --- a/src/status_im/ethereum/subscriptions.cljs +++ b/src/status_im/ethereum/subscriptions.cljs @@ -26,6 +26,7 @@ (fx/defn new-block [{:keys [db] :as cofx} historical? block-number accounts] (let [{:keys [:wallet/all-tokens]} db + accounts (get-in db [:multiaccount :accounts]) ;;TODO https://github.com/status-im/status-go/issues/1566 chain (ethereum/chain-keyword db) chain-tokens (into {} (map (juxt :address identity) (tokens/tokens-for all-tokens chain)))] @@ -34,20 +35,23 @@ (not historical?) (assoc :db (assoc db :ethereum/current-block block-number)) - (not-empty accounts) - (assoc ::transactions/get-transfers {:chain-tokens chain-tokens + true ;(not-empty accounts) ;;TODO https://github.com/status-im/status-go/issues/1566 + (assoc ::transactions/get-transfers {:accounts accounts + :chain-tokens chain-tokens :from-block block-number})) (transactions/check-watched-transactions)))) (fx/defn reorg [{:keys [db] :as cofx} block-number accounts] (let [{:keys [:wallet/all-tokens]} db + accounts (get-in db [:multiaccount :accounts]) ;;TODO https://github.com/status-im/status-go/issues/1566 chain (ethereum/chain-keyword db) chain-tokens (into {} (map (juxt :address identity) (tokens/tokens-for all-tokens chain)))] {:db (update-in db [:wallet :transactions] wallet/remove-transactions-since-block block-number) - ::transactions/get-transfers {:chain-tokens chain-tokens + ::transactions/get-transfers {:accounts accounts + :chain-tokens chain-tokens :from-block block-number}})) (fx/defn new-wallet-event diff --git a/src/status_im/ethereum/transactions/core.cljs b/src/status_im/ethereum/transactions/core.cljs index d7bd7b3e02..df7c98fb6c 100644 --- a/src/status_im/ethereum/transactions/core.cljs +++ b/src/status_im/ethereum/transactions/core.cljs @@ -119,36 +119,26 @@ watched-transactions)))) (fx/defn add-transfer - [{:keys [db] :as cofx} {:keys [hash id] :as transfer}] - (let [transfer-by-hash (get-in db [:wallet :transactions hash]) - transfer-by-id (get-in db [:wallet :transaction id]) - unique-id (when-not (or transfer-by-id - (= transfer transfer-by-hash)) - (if (and transfer-by-hash - (not (= :pending - (:type transfer-by-hash)))) - id - hash))] - (when unique-id + [{:keys [db] :as cofx} {:keys [hash id] :as transfer} address] + (let [transfer-by-hash (get-in db [:wallet :accounts address :transactions hash])] + ;;transfer-by-id (get-in db [:wallet :transaction id]) ;; TODO didn't found any usage of this + (when-let [unique-id (when (not= transfer transfer-by-hash) ;(or transfer-by-id) + (if (and transfer-by-hash + (not (= :pending + (:type transfer-by-hash)))) + id + hash))] (fx/merge cofx - {:db (assoc-in db [:wallet :transactions unique-id] + {:db (assoc-in db [:wallet :accounts address :transactions unique-id] (assoc transfer :hash unique-id))} (check-transaction transfer))))) (fx/defn new-transfers {:events [::new-transfers]} - [{:keys [db] :as cofx} transfers] - (let [add-transfers-fx (map add-transfer transfers)] + [{:keys [db] :as cofx} address transfers] + (let [add-transfers-fx (map #(add-transfer % address) transfers)] (apply fx/merge cofx (conj add-transfers-fx - wallet/update-balances)))) - -(fx/defn handle-history - [{:keys [db] :as cofx} transactions] - (fx/merge cofx - {:db (update-in db - [:wallet :transactions] - #(merge transactions %))} - wallet/update-balances)) + (wallet/update-balances [address]))))) (fx/defn handle-token-history [{:keys [db]} transactions] @@ -158,21 +148,22 @@ (re-frame/reg-fx ::get-transfers - (fn [{:keys [chain-tokens from-block to-block] + (fn [{:keys [accounts chain-tokens from-block to-block] :or {from-block "0" to-block nil}}] ;; start inbound token transaction subscriptions ;; outbound token transactions are already caught in new blocks filter - (json-rpc/call - {:method "wallet_getTransfers" - :params [(encode/uint from-block) (encode/uint to-block)] - :on-success #(re-frame/dispatch - [::new-transfers (enrich-transfers chain-tokens %)])}))) + (doseq [{:keys [address]} accounts] + (json-rpc/call + {:method "wallet_getTransfersByAddress" + :params [address (encode/uint from-block) (encode/uint to-block)] + :on-success #(re-frame/dispatch [::new-transfers address (enrich-transfers chain-tokens %)])})))) (fx/defn initialize [{:keys [db] :as cofx}] - (let [{:keys [:wallet/all-tokens]} db + (let [accounts (get-in db [:multiaccount :accounts]) ;;TODO https://github.com/status-im/status-go/issues/1566 + {:keys [:wallet/all-tokens]} db chain (ethereum/chain-keyword db) chain-tokens (into {} (map (juxt :address identity) (tokens/tokens-for all-tokens chain)))] - {::get-transfers {:chain-tokens chain-tokens}})) + {::get-transfers {:accounts accounts :chain-tokens chain-tokens}})) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index bb5166f90c..cc2a81bb20 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -1788,12 +1788,6 @@ (fn [cofx [_ id handler]] (ethereum.subscriptions/register-subscription cofx id handler))) -;; ethereum transactions events -(handlers/register-handler-fx - :ethereum.transactions.callback/fetch-history-success - (fn [cofx [_ transactions]] - (ethereum.transactions/handle-history cofx transactions))) - (handlers/register-handler-fx :ethereum.transactions.callback/etherscan-error (fn [cofx [event error]] @@ -1834,8 +1828,8 @@ (handlers/register-handler-fx :wallet/token-found - (fn [cofx [_ symbol balance]] - (wallet/configure-token-balance-and-visibility cofx symbol balance))) + (fn [cofx [_ address symbol balance]] + (wallet/configure-token-balance-and-visibility cofx address symbol balance))) (handlers/register-handler-fx :wallet.settings.ui/navigate-back-pressed @@ -1844,12 +1838,12 @@ (when on-close {:dispatch on-close}) (navigation/navigate-back) - (wallet/update-balances)))) + (wallet/update-balances nil)))) (handlers/register-handler-fx :wallet.callback/update-balance-success - (fn [cofx [_ balance]] - (wallet/update-balance cofx balance))) + (fn [cofx [_ address balance]] + (wallet/update-balance cofx address balance))) (handlers/register-handler-fx :wallet.callback/update-balance-fail @@ -1858,8 +1852,8 @@ (handlers/register-handler-fx :wallet.callback/update-token-balance-success - (fn [cofx [_ symbol balance]] - (wallet/update-token-balance cofx symbol balance))) + (fn [cofx [_ address symbol balance]] + (wallet/update-token-balance cofx address symbol balance))) (handlers/register-handler-fx :wallet.callback/update-token-balance-fail @@ -1878,8 +1872,8 @@ (handlers/register-handler-fx :wallet.ui/show-transaction-details - (fn [cofx [_ hash]] - (wallet/open-transaction-details cofx hash))) + (fn [cofx [_ hash address]] + (wallet/open-transaction-details cofx hash address))) (handlers/register-handler-fx :wallet.setup.ui/navigate-back-pressed diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index 47d13120b6..f8007da99a 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -48,11 +48,13 @@ (defn create-multiaccount! [{:keys [id password]}] (if id - (status/multiaccount-store-derived - id - [constants/path-whisper constants/path-default-wallet] - password - #(re-frame/dispatch [:multiaccounts.create.callback/create-multiaccount-success password])) + (do + ;(status/multiaccount-store-account id password #()) ;; TODO if i add this, i'm unable to login after + (status/multiaccount-store-derived + id + [constants/path-whisper constants/path-default-wallet] + password + #(re-frame/dispatch [:multiaccounts.create.callback/create-multiaccount-success password]))) (status/create-multiaccount password #(re-frame/dispatch [:multiaccounts.create.callback/create-multiaccount-success (types/json->clj %) password])))) @@ -243,6 +245,7 @@ new-multiaccount {;;multiaccount :root-address (:address multiaccount) :public-key publicKey + :latest-derived-path 0 :installation-id (get-in db [:multiaccounts/new-installation-id]) ;;TODO why can't we generate it here? :address address :name (gfycat/generate-gfy publicKey) diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index e618759cb9..9f92833103 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -90,7 +90,7 @@ (fx/defn initialize-wallet [cofx] (fx/merge cofx (wallet/initialize-tokens) - (wallet/update-balances) + (wallet/update-balances nil) (wallet/update-prices) (transactions/initialize))) diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 0e681e2f5b..64d6f40418 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -19,6 +19,18 @@ (defn create-multiaccount [password callback] (native-module/create-account password callback)) +(defn multiaccount-store-account [account-id password callback] + (native-module/multiaccount-store-account account-id password callback)) + +(defn multiaccount-load-account [address password callback] + (native-module/multiaccount-load-account address password callback)) + +(defn multiaccount-reset [callback] + (native-module/multiaccount-reset callback)) + +(defn multiaccount-derive-addresses [account-id paths callback] + (native-module/multiaccount-derive-addresses account-id paths callback)) + (defn recover-multiaccount [passphrase password callback] (native-module/recover-account passphrase password callback)) diff --git a/src/status_im/native_module/impl/module.cljs b/src/status_im/native_module/impl/module.cljs index ecb35566b3..cc71a4f56e 100644 --- a/src/status_im/native_module/impl/module.cljs +++ b/src/status_im/native_module/impl/module.cljs @@ -71,6 +71,32 @@ :paths paths}) on-result))) +(defn multiaccount-derive-addresses [account-id paths on-result] + (when (and @node-started (status)) + (.multiAccountDeriveAddresses (status) + (types/clj->json {:accountID account-id + :paths paths}) + on-result))) + +(defn multiaccount-store-account [account-id password on-result] + (when (and @node-started (status)) + (.multiAccountStoreAccount (status) + (types/clj->json {:accountID account-id + :password password}) + on-result))) + +(defn multiaccount-load-account [address password on-result] + (when (and @node-started (status)) + (.multiAccountLoadAccount (status) + (types/clj->json {:address address + :password password}) + on-result))) + +(defn multiaccount-reset [on-result] + (when (and @node-started (status)) + (.multiAccountReset (status) + on-result))) + (defn multiaccount-store-derived [account-id paths password on-result] (when (and @node-started (status)) (.multiAccountStoreDerived (status) diff --git a/src/status_im/signing/core.cljs b/src/status_im/signing/core.cljs index 71f9e95e3f..9d9441eab8 100644 --- a/src/status_im/signing/core.cljs +++ b/src/status_im/signing/core.cljs @@ -137,24 +137,25 @@ :token token :symbol symbol})))))) -(defn parse-tx-obj [db {:keys [to value data]}] - (if (nil? to) - {:contact {:name (i18n/label :t/new-contract)}} - (let [eth-value (when value (money/bignumber value)) - eth-amount (when eth-value (money/to-number (money/wei->ether eth-value))) - token (get-transfer-token db to data)] - (cond - (and eth-amount (or (not (zero? eth-amount)) (nil? data))) - {:to to - :contact (get-contact db to) - :symbol :ETH - :amount (str eth-amount) - :token (tokens/asset-for (:wallet/all-tokens db) (ethereum/chain-keyword db) :ETH)} - (not (nil? token)) - token - :else - {:to to - :contact {:address (ethereum/normalized-address to)}})))) +(defn parse-tx-obj [db {:keys [from to value data]}] + (merge {:from {:address from}} + (if (nil? to) + {:contact {:name (i18n/label :t/new-contract)}} + (let [eth-value (when value (money/bignumber value)) + eth-amount (when eth-value (money/to-number (money/wei->ether eth-value))) + token (get-transfer-token db to data)] + (cond + (and eth-amount (or (not (zero? eth-amount)) (nil? data))) + {:to to + :contact (get-contact db to) + :symbol :ETH + :amount (str eth-amount) + :token (tokens/asset-for (:wallet/all-tokens db) (ethereum/chain-keyword db) :ETH)} + (not (nil? token)) + token + :else + {:to to + :contact {:address (ethereum/normalized-address to)}}))))) (defn prepare-tx [db {{:keys [data gas gasPrice] :as tx-obj} :tx-obj :as tx}] (merge diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index f05774655a..5d6bff47f6 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -181,6 +181,7 @@ (reg-root-key-sub :intro-wizard :intro-wizard) (reg-root-key-sub :popover/popover :popover/popover) +(reg-root-key-sub :generate-account :generate-account) ;;GENERAL ============================================================================================================== @@ -800,6 +801,7 @@ (re-frame/reg-sub :chats/transaction-status + ;;TODO address here for transactions :<- [:wallet/transactions] :<- [:ethereum/current-block] (fn [[transactions current-block] [_ hash]] @@ -965,8 +967,21 @@ (re-frame/reg-sub :balance :<- [:wallet] + (fn [wallet [_ address]] + (get-in wallet [:accounts address :balance]))) + +(re-frame/reg-sub + :balance-default + :<- [:wallet] + :<- [:multiaccount] + (fn [[wallet {:keys [accounts]}]] + (get-in wallet [:accounts (:address (ethereum/get-default-account accounts)) :balance]))) + +(re-frame/reg-sub + :balances + :<- [:wallet] (fn [wallet] - (:balance wallet))) + (map :balance (vals (:accounts wallet))))) (re-frame/reg-sub :price @@ -986,20 +1001,6 @@ (fn [settings] (or (get-in settings [:wallet :currency]) :usd))) -(re-frame/reg-sub - :asset-value - (fn [[_ fsym decimals tsym]] - [(re-frame/subscribe [:balance]) - (re-frame/subscribe [:price fsym tsym]) - (re-frame/subscribe [:wallet/currency])]) - (fn [[balance price currency] [_ fsym decimals tsym]] - (when (and balance price) - (-> (money/internal->formatted (get balance fsym) fsym decimals) - (money/crypto->fiat price) - (money/with-precision 2) - str - (i18n/format-currency (:code currency)))))) - (defn- get-balance-total-value [balance prices currency token->decimals] (reduce-kv (fn [acc symbol value] @@ -1012,21 +1013,39 @@ (re-frame/reg-sub :portfolio-value - :<- [:balance] + :<- [:balances] :<- [:prices] :<- [:wallet/currency] :<- [:ethereum/chain-keyword] :<- [:wallet/all-tokens] - (fn [[balance prices currency chain all-tokens] [_ currency-code]] + (fn [[balances prices currency chain all-tokens]] + (if (and balances prices) + (let [assets (tokens/tokens-for all-tokens chain) + token->decimals (into {} (map #(vector (:symbol %) (:decimals %)) assets)) + currency-key (-> currency :code keyword) + balance-total-value (apply + (map #(get-balance-total-value % prices currency-key token->decimals) balances))] + (if (pos? balance-total-value) + (-> balance-total-value + (money/with-precision 2) + str + (i18n/format-currency (:code currency) false)) + "0")) + "..."))) + +(re-frame/reg-sub + :account-portfolio-value + (fn [[_ address] _] + [(re-frame/subscribe [:balance address]) + (re-frame/subscribe [:prices]) + (re-frame/subscribe [:wallet/currency]) + (re-frame/subscribe [:ethereum/chain-keyword]) + (re-frame/subscribe [:wallet/all-tokens])]) + (fn [[balance prices currency chain all-tokens]] (if (and balance prices) - (let [assets (tokens/tokens-for all-tokens chain) - token->decimals (into {} (map #(vector (:symbol %) (:decimals %)) assets)) - balance-total-value - (get-balance-total-value balance - prices - (or currency-code - (-> currency :code keyword)) - token->decimals)] + (let [assets (tokens/tokens-for all-tokens chain) + token->decimals (into {} (map #(vector (:symbol %) (:decimals %)) assets)) + currency-key (-> currency :code keyword) + balance-total-value (get-balance-total-value balance prices currency-key token->decimals)] (if (pos? balance-total-value) (-> balance-total-value (money/with-precision 2) @@ -1050,12 +1069,6 @@ (let [vt-set (set visible-tokens)] (group-by :custom? (map #(assoc % :checked? (boolean (get vt-set (keyword (:symbol %))))) all-tokens))))) -(re-frame/reg-sub - :wallet/balance-loading? - :<- [:wallet] - (fn [wallet] - (:balance-loading? wallet))) - (re-frame/reg-sub :wallet/error-message :<- [:wallet] @@ -1082,18 +1095,19 @@ (re-frame/reg-sub :wallet/visible-assets-with-amount - :<- [:balance] - :<- [:wallet/visible-assets] + (fn [[_ address] _] + [(re-frame/subscribe [:balance address]) + (re-frame/subscribe [:wallet/visible-assets])]) (fn [[balance visible-assets]] (map #(assoc % :amount (get balance (:symbol %))) visible-assets))) -(defn update-value [balance prices currency] - (fn [{:keys [symbol decimals] :as token}] +(defn update-value [prices currency] + (fn [{:keys [symbol decimals amount] :as token}] (let [price (get-in prices [symbol (-> currency :code keyword) :price])] (assoc token :price price - :value (when (and balance price) - (-> (money/internal->formatted (get balance symbol) symbol decimals) + :value (when (and amount price) + (-> (money/internal->formatted amount symbol decimals) (money/crypto->fiat price) (money/with-precision 2) str @@ -1101,19 +1115,45 @@ (re-frame/reg-sub :wallet/visible-assets-with-values - :<- [:wallet/visible-assets-with-amount] + (fn [[_ address] _] + [(re-frame/subscribe [:wallet/visible-assets-with-amount address]) + (re-frame/subscribe [:prices]) + (re-frame/subscribe [:wallet/currency])]) + (fn [[assets prices currency]] + (let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets) + tokens-with-values (map (update-value prices currency) tokens)] + {:tokens tokens-with-values + :nfts nfts}))) + +(defn get-asset-amount [balances sym] + (reduce #(if-let [bl (get %2 sym)] + (.plus %1 bl) + %1) + (money/bignumber 0) + balances)) + +(re-frame/reg-sub + :wallet/all-visible-assets-with-amount + :<- [:balances] + :<- [:wallet/visible-assets] + (fn [[balances visible-assets]] + (map #(assoc % :amount (get-asset-amount balances (:symbol %))) visible-assets))) + +(re-frame/reg-sub + :wallet/all-visible-assets-with-values + :<- [:wallet/all-visible-assets-with-amount] :<- [:prices] :<- [:wallet/currency] - :<- [:balance] - (fn [[assets prices currency balance]] + (fn [[assets prices currency]] (let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets) - tokens-with-values (map (update-value balance prices currency) tokens)] + tokens-with-values (map (update-value prices currency) tokens)] {:tokens tokens-with-values :nfts nfts}))) (re-frame/reg-sub :wallet/transferrable-assets-with-amount - :<- [:wallet/visible-assets-with-amount] + (fn [[_ address]] + (re-frame/subscribe [:wallet/visible-assets-with-amount address])) (fn [all-assets] (filter #(not (:nft? %)) all-assets))) @@ -1128,8 +1168,8 @@ (re-frame/reg-sub :wallet/transactions :<- [:wallet] - (fn [wallet] - (get wallet :transactions))) + (fn [wallet [_ address]] + (get-in wallet [:accounts address :transactions]))) (re-frame/reg-sub :wallet/filters @@ -1161,14 +1201,15 @@ (re-frame/reg-sub :wallet.transactions/transactions - :<- [:wallet/transactions] - :<- [:contacts/contacts-by-address] - :<- [:ethereum/native-currency] + (fn [[_ address] _] + [(re-frame/subscribe [:wallet/transactions address]) + (re-frame/subscribe [:contacts/contacts-by-address]) + (re-frame/subscribe [:ethereum/native-currency])]) (fn [[transactions contacts native-currency]] (reduce (fn [acc [hash transaction]] (assoc acc hash - (enrich-transaction transaction contacts native-currency))) + (enrich-transaction transaction contacts native-currency))) ;;TODO this doesn't look good for performance, we need to calculate this only once for each transaction {} transactions))) @@ -1212,7 +1253,8 @@ (defn- enrich-transaction-for-list [filters - {:keys [type from-contact from to-contact to hash timestamp] :as transaction}] + {:keys [type from-contact from to-contact to hash timestamp] :as transaction} + address] (when (filters type) (assoc (case type :inbound @@ -1229,7 +1271,7 @@ :contact to-contact :address to)) :time-formatted (datetime/timestamp->time timestamp) - :on-touch-fn #(re-frame/dispatch [:wallet.ui/show-transaction-details hash])))) + :on-touch-fn #(re-frame/dispatch [:wallet.ui/show-transaction-details hash address])))) (defn- group-transactions-by-date [transactions] @@ -1243,33 +1285,28 @@ (re-frame/reg-sub :wallet.transactions.history/screen - :<- [:wallet.transactions/transactions] - :<- [:wallet/filters] - :<- [:wallet.transactions/all-filters?] - (fn [[transactions filters all-filters?]] + (fn [[_ address] _] + [(re-frame/subscribe [:wallet.transactions/transactions address]) + (re-frame/subscribe [:wallet/filters]) + (re-frame/subscribe [:wallet.transactions/all-filters?])]) + (fn [[transactions filters all-filters?] [_ address]] {:all-filters? all-filters? :transaction-history-sections (->> transactions vals - (keep #(enrich-transaction-for-list filters %)) + (keep #(enrich-transaction-for-list filters % address)) (group-transactions-by-date))})) -(re-frame/reg-sub - :wallet.transactions/current-transaction - :<- [:wallet] - (fn [wallet] - (:current-transaction wallet))) - (re-frame/reg-sub :wallet.transactions.details/current-transaction - :<- [:wallet.transactions/transactions] - :<- [:wallet.transactions/current-transaction] - :<- [:ethereum/native-currency] - :<- [:ethereum/chain-keyword] - (fn [[transactions current-transaction native-currency chain-keyword]] + (fn [[_ hash address] _] + [(re-frame/subscribe [:wallet.transactions/transactions address]) + (re-frame/subscribe [:ethereum/native-currency]) + (re-frame/subscribe [:ethereum/chain-keyword])]) + (fn [[transactions native-currency chain-keyword] [_ hash _]] (let [{:keys [gas-used gas-price hash timestamp type token value] :as transaction} - (get transactions current-transaction) + (get transactions hash) native-currency-text (name (or (:symbol-display native-currency) (:symbol native-currency)))] (when transaction @@ -1301,8 +1338,9 @@ (re-frame/reg-sub :wallet.transactions.details/screen - :<- [:wallet.transactions.details/current-transaction] - :<- [:ethereum/current-block] + (fn [[_ hash address] _] + [(re-frame/subscribe [:wallet.transactions.details/current-transaction hash address]) + (re-frame/subscribe [:ethereum/current-block])]) (fn [[transaction current-block]] (let [confirmations (wallet.db/get-confirmations transaction current-block)] @@ -1358,11 +1396,12 @@ (re-frame/reg-sub :wallet.send/transaction :<- [::send-transaction] - :<- [:balance] - (fn [[{:keys [amount symbol] :as transaction} balance]] - (-> transaction - (check-sufficient-funds balance symbol amount) - (check-sufficient-gas balance symbol amount)))) + :<- [:wallet] + (fn [[{:keys [amount symbol from] :as transaction} wallet]] + (let [balance (get-in wallet [:accounts from :balance])] + (-> transaction + (check-sufficient-funds balance symbol amount) + (check-sufficient-gas balance symbol amount))))) (re-frame/reg-sub :wallet/settings @@ -1960,8 +1999,9 @@ (re-frame/reg-sub :signing/amount-errors - :<- [:signing/tx] - :<- [:balance] + (fn [[_ address] _] + [(re-frame/subscribe [:signing/tx]) + (re-frame/subscribe [:balance address])]) (fn [[{:keys [amount token gas gasPrice approve?]} balance]] (if (and amount token (not approve?)) (let [amount-bn (money/formatted->internal (money/bignumber amount) (:symbol token) (:decimals token)) diff --git a/src/status_im/ui/components/colors.cljs b/src/status_im/ui/components/colors.cljs index 274cda46de..2217fcbe9c 100644 --- a/src/status_im/ui/components/colors.cljs +++ b/src/status_im/ui/components/colors.cljs @@ -57,3 +57,11 @@ (def text-gray gray) (def default-chat-color "#a187d5") ;; legacy + +(def account-colors ["#9B832F" + "#D37EF4" + "#1D806F" + "#FA6565" + "#7CDA00" + "#887AF9" + "#8B3131"]) diff --git a/src/status_im/ui/components/list_item/views.cljs b/src/status_im/ui/components/list_item/views.cljs index 91ae5c1bf1..f03d15c687 100644 --- a/src/status_im/ui/components/list_item/views.cljs +++ b/src/status_im/ui/components/list_item/views.cljs @@ -4,15 +4,16 @@ [status-im.ui.components.react :as react] [status-im.ui.components.list-item.styles :as styles] [status-im.utils.image :as utils.image] - [status-im.ui.components.tooltip.views :as tooltip])) + [status-im.ui.components.tooltip.views :as tooltip] + [status-im.ui.components.action-button.styles :as st])) ; type - optional :default , :small ; accessories - optional vector of :chevron, :check or component or string -; theme - optional :default, :wallet +; theme - optional :default, :wallet, :action -(defn list-item [{:keys [title subtitle accessories image image-path type theme on-press error content] :or {type :default theme :default}}] +(defn list-item [{:keys [title subtitle accessories image image-path icon type theme on-press error content] :or {type :default theme :default}}] (let [small? (= :small type)] [react/touchable-highlight {:on-press on-press :disabled (not on-press)} [react/view {:style (styles/container small?)} @@ -22,6 +23,13 @@ (if (vector? image) image [image])]) + ;;Icon + (when icon + [react/view {:margin-left 16} + [react/view (when (= theme :action) (st/action-button-icon-container nil)) + [icons/icon icon (if (= theme :action) + st/action-button-label + {:color colors/gray-transparent-40})]]]) (when image-path [react/view {:margin-left 16} [react/image {:source (utils.image/source image-path) @@ -29,7 +37,8 @@ ;;Title (when title [react/view {:style {:margin-left 16 :margin-right 16}} - [react/text {:style (styles/title small? subtitle) + [react/text {:style (merge (styles/title small? subtitle) + (when (= theme :action) st/action-button-label)) :number-of-lines 1 :ellipsize-mode :tail} title] diff --git a/src/status_im/ui/components/status_bar/view.cljs b/src/status_im/ui/components/status_bar/view.cljs index 39a86727d4..37b9578ee2 100644 --- a/src/status_im/ui/components/status_bar/view.cljs +++ b/src/status_im/ui/components/status_bar/view.cljs @@ -60,6 +60,7 @@ :wallet-send-assets {:type :wallet} :wallet-request-assets {:type :wallet} :recent-recipients {:type :wallet} + :select-account {:type :wallet} :contact-code {:type :wallet} :wallet-send-transaction-request {:type :wallet} :wallet-settings-assets {:type :main} diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index 6921a45830..6af3cf9b70 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -358,4 +358,5 @@ ::collectibles ::extensions-store :registry/registry - ::two-pane-ui-enabled?])) + ::two-pane-ui-enabled? + ::generate-account])) diff --git a/src/status_im/ui/screens/popover/views.cljs b/src/status_im/ui/screens/popover/views.cljs index 76b5bddb8a..594ad9a98b 100644 --- a/src/status_im/ui/screens/popover/views.cljs +++ b/src/status_im/ui/screens/popover/views.cljs @@ -55,7 +55,7 @@ (hide-panel-anim bottom-anim-value alpha-value (- window-height))))) :reagent-render (fn [] (when @current-popover - (let [view (:view @current-popover)] + (let [{:keys [view style]} @current-popover] [react/view {:position :absolute :top 0 :bottom 0 :left 0 :right 0} [react/animated-view {:style {:flex 1 :background-color :black :opacity alpha-value}}] [react/animated-view {:style @@ -66,13 +66,14 @@ :transform [{:translateY bottom-anim-value}]}} [react/touchable-highlight {:style {:flex 1} :on-press #(re-frame/dispatch [:hide-popover])} [react/view {:flex 1 :align-items :center :justify-content :center} - [react/view {:background-color :white - :border-radius 16 - :margin 32 - :shadow-offset {:width 0 :height 2} - :shadow-radius 8 - :shadow-opacity 1 - :shadow-color "rgba(0, 9, 26, 0.12)"} + [react/view (merge {:background-color :white + :border-radius 16 + :margin 32 + :shadow-offset {:width 0 :height 2} + :shadow-radius 8 + :shadow-opacity 1 + :shadow-color "rgba(0, 9, 26, 0.12)"} + style) (cond (vector? view) view diff --git a/src/status_im/ui/screens/routing/screens.cljs b/src/status_im/ui/screens/routing/screens.cljs index 8031f35a34..4aa2551e87 100644 --- a/src/status_im/ui/screens/routing/screens.cljs +++ b/src/status_im/ui/screens/routing/screens.cljs @@ -64,7 +64,8 @@ [status-im.ui.screens.wallet.transactions.views :as wallet-transactions] [status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens] [status-im.ui.screens.wallet.accounts.views :as wallet.accounts] - [status-im.ui.screens.wallet.account.views :as wallet.account])) + [status-im.ui.screens.wallet.account.views :as wallet.account] + [status-im.ui.screens.wallet.add-new.views :as add-account])) (def all-screens {:login login/login @@ -140,12 +141,11 @@ :contact-code wallet.components/contact-code :wallet-send-transaction send/send-transaction :recent-recipients wallet.components/recent-recipients + :select-account wallet.components/accounts :recipient-qr-code wallet.components/recipient-qr-code :wallet-send-assets wallet.components/send-assets :wallet-send-transaction-request request/send-transaction-request :wallet-request-assets wallet.components/request-assets - :unsigned-transactions wallet-transactions/transactions - :transactions-history wallet-transactions/transactions :wallet-transaction-details wallet-transactions/transaction-details :wallet-settings-hook wallet-settings/settings-hook :selection-modal-screen [:modal extensions.module/selection-modal-screen-view] @@ -186,7 +186,10 @@ :keycard-settings hardwallet.settings/keycard-settings :mobile-network-settings mobile-network-settings/mobile-network-settings :welcome [:modal home/welcome] - :keycard-welcome keycard/welcome}) + :keycard-welcome keycard/welcome + :add-new-account add-account/add-account + :add-new-account-password add-account/password + :account-added add-account/account-added}) (defn get-screen [screen] (get all-screens screen #(throw (str "Screen " screen " is not defined.")))) diff --git a/src/status_im/ui/screens/routing/wallet_stack.cljs b/src/status_im/ui/screens/routing/wallet_stack.cljs index afa5818bb5..2bd739289b 100644 --- a/src/status_im/ui/screens/routing/wallet_stack.cljs +++ b/src/status_im/ui/screens/routing/wallet_stack.cljs @@ -4,12 +4,16 @@ {:name :wallet-stack :screens [:wallet :wallet-account + :add-new-account + :add-new-account-password + :account-added :collectibles-list :wallet-onboarding-setup :contact-code {:name :send-transaction-stack :screens [:wallet-send-transaction :recent-recipients + :select-account :enter-pin-sign :hardwallet-connect-sign :recipient-qr-code @@ -18,8 +22,6 @@ :screens [:wallet-send-transaction-request :wallet-request-assets :recent-recipients]} - :unsigned-transactions - :transactions-history :wallet-transaction-details :wallet-settings-hook :extension-screen-holder diff --git a/src/status_im/ui/screens/signing/views.cljs b/src/status_im/ui/screens/signing/views.cljs index 157fd764fd..269a58ed6c 100644 --- a/src/status_im/ui/screens/signing/views.cljs +++ b/src/status_im/ui/screens/signing/views.cljs @@ -55,9 +55,9 @@ (multiaccounts/displayed-name contact) (:address contact))) -(defn contact-item [contact] +(defn contact-item [title contact] [list-item/list-item {:type :small - :title (i18n/label :t/to) + :title title :accessories [[react/text {:ellipsize-mode :middle :number-of-lines 1 :style {:flex-wrap :wrap}} (displayed-name contact)]]}]) @@ -205,11 +205,11 @@ [react/text (or formatted-data "")]]] [password-view sign]]])) -(views/defview sheet [{:keys [contact amount token approve?] :as tx}] +(views/defview sheet [{:keys [from contact amount token approve?] :as tx}] (views/letsubs [fee [:signing/fee] sign [:signing/sign] chain [:ethereum/chain-keyword] - {:keys [amount-error gas-error]} [:signing/amount-errors] + {:keys [amount-error gas-error]} [:signing/amount-errors (:address from)] keycard-multiaccount? [:keycard-multiaccount?]] (let [display-symbol (wallet.utils/display-symbol token) fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))] @@ -220,7 +220,9 @@ [react/view {:padding-top 20} [password-view sign]] [react/view - [contact-item contact] + [contact-item (i18n/label :t/from) from] + [separator] + [contact-item (i18n/label :t/to) contact] [separator] [token-item token display-symbol] (when-not approve? @@ -286,4 +288,4 @@ (views/letsubs [tx [:signing/tx] {window-height :height} [:dimensions/window]] ;;we use select-keys here because we don't want to update view if other keys in map is changed - [signing-view (when tx (select-keys tx [:contact :amount :token :approve? :message])) window-height])) + [signing-view (when tx (select-keys tx [:from :contact :amount :token :approve? :message])) window-height])) diff --git a/src/status_im/ui/screens/stickers/views.cljs b/src/status_im/ui/screens/stickers/views.cljs index 2a6ce976c2..03ec6495ec 100644 --- a/src/status_im/ui/screens/stickers/views.cljs +++ b/src/status_im/ui/screens/stickers/views.cljs @@ -23,7 +23,7 @@ (defview price-badge [price id owned pending] (letsubs [chain [:ethereum/chain-keyword] - balance [:balance]] + balance [:balance-default]] (let [snt (money/to-number (if (= :mainnet chain) (:SNT balance) (:STT balance))) not-enough-snt? (> price snt) no-snt? (or (nil? snt) (zero? snt))] diff --git a/src/status_im/ui/screens/wallet/account/styles.cljs b/src/status_im/ui/screens/wallet/account/styles.cljs index 67db08c680..7f32f2be6b 100644 --- a/src/status_im/ui/screens/wallet/account/styles.cljs +++ b/src/status_im/ui/screens/wallet/account/styles.cljs @@ -1,9 +1,10 @@ (ns status-im.ui.screens.wallet.account.styles (:require [status-im.ui.components.colors :as colors])) -(defn card [window-width] - {:width (- window-width 64) :height 161 - :background-color colors/blue +(defn card [window-width color] + {:width (- window-width 30) + :height 161 + :background-color color :shadow-offset {:width 0 :height 2} :shadow-radius 8 :shadow-opacity 1 diff --git a/src/status_im/ui/screens/wallet/account/views.cljs b/src/status_im/ui/screens/wallet/account/views.cljs index af273ee668..2931695128 100644 --- a/src/status_im/ui/screens/wallet/account/views.cljs +++ b/src/status_im/ui/screens/wallet/account/views.cljs @@ -38,11 +38,11 @@ [icons/icon icon {:color colors/white}] [react/text {:style {:margin-left 8 :color colors/white}} label]]]]) -(views/defview account-card [{:keys [address]}] +(views/defview account-card [{:keys [address color]}] (views/letsubs [currency [:wallet/currency] - portfolio-value [:portfolio-value] + portfolio-value [:account-portfolio-value address] window-width [:dimensions/window-width]] - [react/view {:style (styles/card window-width)} + [react/view {:style (styles/card window-width color)} [react/view {:padding 16 :padding-bottom 12 :flex 1 :justify-content :space-between} [react/nested-text {:style {:color colors/white-transparent :line-height 38 :font-weight "600" :font-size 32}} @@ -66,20 +66,19 @@ [react/view {:style styles/divider}] [button (i18n/label :t/receive) :main-icons/receive #(re-frame/dispatch [:show-popover {:view :share-account :address address}])]]])) -(views/defview transactions [] +(views/defview transactions [address] (views/letsubs [{:keys [transaction-history-sections]} - [:wallet.transactions.history/screen]] + [:wallet.transactions.history/screen address]] [history/history-list transaction-history-sections])) -(views/defview assets-and-collections [] - (views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values] +(views/defview assets-and-collections [address] + (views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values address] currency [:wallet/currency]] (let [{:keys [tab]} @state] [react/view {:flex 1} [react/view {:flex-direction :row :margin-bottom 8 :padding-horizontal 4} [accounts/tab-title state :assets (i18n/label :t/wallet-assets) (= tab :assets)] - (when (seq nfts) - [accounts/tab-title state :nft (i18n/label :t/wallet-collectibles) (= tab :nft)]) + [accounts/tab-title state :nft (i18n/label :t/wallet-collectibles) (= tab :nft)] [accounts/tab-title state :history (i18n/label :t/history) (= tab :history)]] (cond (= tab :assets) @@ -91,18 +90,22 @@ :align-self :stretch}}] :render-fn (accounts/render-asset (:code currency))}] (= tab :nft) - [list/flat-list {:data nfts - :default-separator? false - :key-fn :name - :footer [react/view - {:style {:height tabs.styles/tabs-diff - :align-self :stretch}}] - :render-fn accounts/render-collectible}] + (if (seq nfts) + [list/flat-list {:data nfts + :default-separator? false + :key-fn :name + :footer [react/view + {:style {:height tabs.styles/tabs-diff + :align-self :stretch}}] + :render-fn accounts/render-collectible}] + [react/view {:align-items :center :margin-top 32} + [react/text {:style {:color colors/gray}} + (i18n/label :t/no-collectibles)]]) (= tab :history) - [transactions])]))) + [transactions address])]))) (views/defview account [] - (views/letsubs [{:keys [name] :as account} [:get-screen-params]] + (views/letsubs [{:keys [name address] :as account} [:get-screen-params]] [react/view {:flex 1 :background-color colors/white} [toolbar-view name] [react/scroll-view @@ -110,4 +113,4 @@ [react/scroll-view {:horizontal true} [react/view {:flex-direction :row :padding-top 8 :padding-bottom 12} [account-card account]]]] - [assets-and-collections]]])) \ No newline at end of file + [assets-and-collections address]]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/accounts/sheets.cljs b/src/status_im/ui/screens/wallet/accounts/sheets.cljs index 9a3ee7a0f0..214a3fa8bb 100644 --- a/src/status_im/ui/screens/wallet/accounts/sheets.cljs +++ b/src/status_im/ui/screens/wallet/accounts/sheets.cljs @@ -50,10 +50,10 @@ (defn add-account [] [react/view - [action-button/action-button-disabled {:label (i18n/label :t/add-an-account) - :icon :main-icons/add - :icon-opts {:color :blue} - :on-press #(hide-sheet-and-dispatch [:navigate-to])}] + [action-button/action-button {:label (i18n/label :t/add-an-account) + :icon :main-icons/add + :icon-opts {:color :blue} + :on-press #(hide-sheet-and-dispatch [:navigate-to :add-new-account])}] [action-button/action-button-disabled {:label (i18n/label :t/add-a-watch-account) :icon :main-icons/watch :icon-opts {:color :blue} diff --git a/src/status_im/ui/screens/wallet/accounts/styles.cljs b/src/status_im/ui/screens/wallet/accounts/styles.cljs index 2288bb4fc2..eae8e348e1 100644 --- a/src/status_im/ui/screens/wallet/accounts/styles.cljs +++ b/src/status_im/ui/screens/wallet/accounts/styles.cljs @@ -4,6 +4,7 @@ (defn card [color] {:width 156 :height 145 + :margin-right 16 :background-color color :shadow-offset {:width 0 :height 2} :shadow-radius 8 @@ -20,7 +21,6 @@ (def add-card {:width 156 :height 145 - :margin-left 16 :margin-top 5 :margin-right 5 :margin-bottom 5 diff --git a/src/status_im/ui/screens/wallet/accounts/views.cljs b/src/status_im/ui/screens/wallet/accounts/views.cljs index 2c1fce4575..5c206c28b0 100644 --- a/src/status_im/ui/screens/wallet/accounts/views.cljs +++ b/src/status_im/ui/screens/wallet/accounts/views.cljs @@ -25,7 +25,7 @@ (views/defview account-card [{:keys [name color address] :as account}] (views/letsubs [currency [:wallet/currency] - portfolio-value [:portfolio-value]] + portfolio-value [:account-portfolio-value address]] [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :wallet-account account]) :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] [sheets/send-receive address]) @@ -97,7 +97,7 @@ :accessories [items-number :chevron]}]])) (views/defview assets-and-collections [] - (views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values] + (views/letsubs [{:keys [tokens nfts]} [:wallet/all-visible-assets-with-values] currency [:wallet/currency]] (let [{:keys [tab]} @state] [react/view {:flex 1} diff --git a/src/status_im/ui/screens/wallet/add_new/views.cljs b/src/status_im/ui/screens/wallet/add_new/views.cljs new file mode 100644 index 0000000000..2bea99f404 --- /dev/null +++ b/src/status_im/ui/screens/wallet/add_new/views.cljs @@ -0,0 +1,127 @@ +(ns status-im.ui.screens.wallet.add-new.views + (:require-macros [status-im.utils.views :refer [defview letsubs]]) + (:require [status-im.ui.components.react :as react] + [status-im.ui.components.status-bar.view :as status-bar] + [status-im.ui.components.toolbar.view :as toolbar] + [status-im.i18n :as i18n] + [re-frame.core :as re-frame] + [status-im.ui.components.colors :as colors] + [status-im.ui.components.list-header.views :as list-header] + [status-im.ui.components.list-item.views :as list-item] + [status-im.ui.components.common.common :as components.common] + [status-im.ui.components.icons.vector-icons :as icons] + [status-im.ui.components.text-input.view :as text-input] + [reagent.core :as reagent] + [clojure.string :as string] + [cljs.spec.alpha :as spec] + [status-im.multiaccounts.db :as multiaccounts.db])) + +(defn add-account [] + [react/view {:flex 1} + [status-bar/status-bar] + [toolbar/toolbar {:transparent? true} toolbar/default-nav-back nil] + [react/scroll-view {:keyboard-should-persist-taps :handled + :style {:flex 1}} + [react/view {:align-items :center :padding-horizontal 40} + [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)]] + [react/view {:height 52}] + [list-header/list-header (i18n/label :t/default)] + [list-item/list-item {:title (i18n/label :t/generate-a-new-account) :theme :action + :icon :main-icons/add :accessories [:chevron] + :on-press #(re-frame/dispatch [:navigate-to :add-new-account-password])}]]]) + +(defview colors-popover [selected-color] + (letsubs [width [:dimensions/window-width]] + [react/view {:padding-bottom 16} + [react/scroll-view {:style {:margin 16}} + (doall + (for [color colors/account-colors] + ^{:key color} + [react/touchable-highlight {:on-press #(do + (re-frame/dispatch [:set-in [:generate-account :account :color] color]) + (re-frame/dispatch [:hide-popover]))} + [react/view {:height 52 :background-color color :border-radius 8 :width (* 0.7 width) + :justify-content :center :padding-left 12 :margin-bottom 16} + [react/view {:height 32 :width 32 :border-radius 20 :align-items :center :justify-content :center + :background-color colors/black-transparent} + (when (= selected-color color) + [icons/icon :main-icons/check {:color colors/white}])]]]))] + [components.common/button {:on-press #(re-frame/dispatch [:hide-popover]) + :label (i18n/label :t/cancel) + :background? false}]])) + +(defview account-added [] + (letsubs [{:keys [account]} [:generate-account]] + [react/keyboard-avoiding-view {:flex 1} + [status-bar/status-bar] + [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) + :on-change-text #(re-frame/dispatch [:set-in [:generate-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)] + :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}]]]]] + [react/view {:style {:flex-direction :row + :justify-content :flex-end + :align-self :stretch + :padding-vertical 16 + :border-top-width 1 + :border-top-color colors/gray-lighter + :padding-right 12}} + [components.common/bottom-button {:label (i18n/label :t/finish) + :on-press #(re-frame/dispatch [:wallet.accounts/save-generated-account]) + :disabled? (string/blank? (:name account)) + :forward? true}]]])) + +(defview password [] + (letsubs [{:keys [error]} [:generate-account] + entered-password (reagent/atom "")] + [react/keyboard-avoiding-view {:style {:flex 1}} + [status-bar/status-bar {:flat? true}] + [toolbar/toolbar {:transparent? true} toolbar/default-nav-back nil] + [react/view {:flex 1} + [react/view {:style {: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 {:style {:justify-content :center :flex 1}} + [react/text-input {:secure-text-entry true + :auto-focus true + :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)]] + [react/view {:style {:flex-direction :row + :justify-content :flex-end + :align-self :stretch + :padding-vertical 16 + :border-top-width 1 + :border-top-color colors/gray-lighter + :padding-right 12}} + [components.common/bottom-button {:label (i18n/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)) + :forward? true}]]]])) \ No newline at end of file diff --git a/src/status_im/ui/screens/wallet/components/views.cljs b/src/status_im/ui/screens/wallet/components/views.cljs index 168c7cdf47..5b7f1dfe20 100644 --- a/src/status_im/ui/screens/wallet/components/views.cljs +++ b/src/status_im/ui/screens/wallet/components/views.cljs @@ -29,7 +29,8 @@ [status-im.wallet.utils :as wallet.utils] [status-im.utils.core :as utils.core] [status-im.utils.money :as money] - [status-im.utils.utils :as utils.utils]) + [status-im.utils.utils :as utils.utils] + [status-im.ui.components.chat-icon.screen :as chat-icon.screen]) (:require-macros [status-im.utils.views :as views])) ;; Wallet tab has a different coloring scheme (dark) that forces color changes (background, text) @@ -120,8 +121,8 @@ (wallet.utils/display-symbol token)]] [list/item-secondary (wallet.utils/format-amount amount decimals)]]]]]) -(views/defview assets [type] - (views/letsubs [assets [:wallet/transferrable-assets-with-amount]] +(views/defview assets [type address] + (views/letsubs [assets [:wallet/transferrable-assets-with-amount address]] [simple-screen [toolbar (i18n/label :t/wallet-assets)] [react/view {:style (assoc components.styles/flex :background-color :white)} @@ -130,11 +131,13 @@ :key-fn (comp str :symbol) :render-fn #(render-token % type)}]]])) -(defn send-assets [] - [assets :send]) +(views/defview send-assets [] + (views/letsubs [address [:get-screen-params]] + [assets :send address])) -(defn request-assets [] - [assets :request]) +(views/defview request-assets [] + (views/letsubs [address [:get-screen-params]] + [assets :request address])) (defn- type->view [k] (case k @@ -142,14 +145,14 @@ :request :wallet-request-assets (throw (str "Unknown type: " k)))) -(views/defview asset-selector [{:keys [disabled? type symbol error]}] - (views/letsubs [balance [:balance] +(views/defview asset-selector [{:keys [disabled? type symbol error address]}] + (views/letsubs [balance [:balance address] chain [:ethereum/chain-keyword] all-tokens [:wallet/all-tokens]] (let [{:keys [name icon decimals color] :as token} (tokens/asset-for all-tokens chain symbol)] (when name [react/view - [cartouche {:disabled? disabled? :on-press #(re-frame/dispatch [:navigate-to (type->view type)])} + [cartouche {:disabled? disabled? :on-press #(re-frame/dispatch [:navigate-to (type->view type) address])} (i18n/label :t/wallet-asset) [react/view {:style styles/asset-content-container :accessibility-label :choose-asset-button} @@ -202,6 +205,17 @@ :accessibility-label :contact-address-text} (eip55/address->checksum (ethereum/normalized-address (:address contact)))]]]]) +(defn render-account [account] + [list/touchable-item #(re-frame/dispatch [:wallet/fill-request-from-contact account false]) + [list/item + [chat-icon/custom-icon-view-list (:name account) (:color account)] + [list/item-content + [list/item-primary {:accessibility-label :contact-name-text} + (:name account)] + [react/text {:style list.styles/secondary-text + :accessibility-label :contact-address-text} + (eip55/address->checksum (ethereum/normalized-address (:address account)))]]]]) + (views/defview recent-recipients [] (views/letsubs [contacts [:contacts/active] {:keys [request?]} [:get-screen-params :recent-recipients]] @@ -212,6 +226,15 @@ :key-fn :address :render-fn #(render-contact % request?)}]]])) +(views/defview accounts [] + (views/letsubs [{:keys [accounts]} [:multiaccount]] + [simple-screen + [toolbar (i18n/label :t/accounts)] + [react/view styles/recent-recipients + [list/flat-list {:data accounts + :key-fn :address + :render-fn render-account}]]])) + (defn contact-code [] (let [content (reagent/atom nil)] (fn [] @@ -252,7 +275,9 @@ [{:label (i18n/label :t/recent-recipients) :action #(re-frame/dispatch [:navigate-to :recent-recipients {:request? request?}])}] (when-not contact-only?)) - :options [{:label (i18n/label :t/scan-qr) + :options [{:label (i18n/label :t/accounts) + :action #(re-frame/dispatch [:navigate-to :select-account])} + {:label (i18n/label :t/scan-qr) :action request-camera-permissions} {:label (i18n/label :t/recipient-code) :action #(re-frame/dispatch [:navigate-to :contact-code])}]})) diff --git a/src/status_im/ui/screens/wallet/custom_tokens/views.cljs b/src/status_im/ui/screens/wallet/custom_tokens/views.cljs index d170b8e8e0..a2bfb7f481 100644 --- a/src/status_im/ui/screens/wallet/custom_tokens/views.cljs +++ b/src/status_im/ui/screens/wallet/custom_tokens/views.cljs @@ -82,12 +82,12 @@ :auto-focus false :placeholder "18"}]]] [react/view {:height 16}] - [text-input/text-input-with-label - {:label (i18n/label :t/balance) - :default-value (when (and balance decimals) - (wallet.utils/format-amount balance decimals)) - :editable false - :placeholder (i18n/label :t/no-tokens-found)}]] + #_[text-input/text-input-with-label + {:label (i18n/label :t/balance) + :default-value (when (and balance decimals) + (wallet.utils/format-amount balance decimals)) + :editable false + :placeholder (i18n/label :t/no-tokens-found)}]] [react/view {:style {:height 1 :background-color colors/gray-lighter}}] [react/view {:flex-direction :row :margin-horizontal 12 diff --git a/src/status_im/ui/screens/wallet/navigation.cljs b/src/status_im/ui/screens/wallet/navigation.cljs index b2008cd838..96ee4818e8 100644 --- a/src/status_im/ui/screens/wallet/navigation.cljs +++ b/src/status_im/ui/screens/wallet/navigation.cljs @@ -32,3 +32,7 @@ (defmethod navigation/preload-data! :wallet-add-custom-token [db [event]] (dissoc db :wallet/custom-token-screen)) + +(defmethod navigation/preload-data! :add-new-account + [db [event]] + (dissoc db :generate-account)) diff --git a/src/status_im/ui/screens/wallet/request/views.cljs b/src/status_im/ui/screens/wallet/request/views.cljs index ad890a2df3..c81f1da796 100644 --- a/src/status_im/ui/screens/wallet/request/views.cljs +++ b/src/status_im/ui/screens/wallet/request/views.cljs @@ -80,7 +80,7 @@ (eip55/address->checksum address)]]] [react/view {:height 1 :background-color colors/gray-lighter :margin-top 8}] [react/view {:padding-bottom 16} - [components.common/button {:on-press #(re-frame/dispatch [:wallet.accounts/share]) + [components.common/button {:on-press #(re-frame/dispatch [:wallet.accounts/share address]) :button-style {:margin-vertical 20 :margin-horizontal 16} :accessibility-label :share-address-button :label (i18n/label :t/share-address)}] diff --git a/src/status_im/ui/screens/wallet/send/views.cljs b/src/status_im/ui/screens/wallet/send/views.cljs index 0e21caaf9b..a9a3d80089 100644 --- a/src/status_im/ui/screens/wallet/send/views.cljs +++ b/src/status_im/ui/screens/wallet/send/views.cljs @@ -36,7 +36,7 @@ colors/white-light-transparent)}]]]) (defn- render-send-transaction-view [{:keys [chain transaction scroll all-tokens amount-input network-status]}] - (let [{:keys [amount amount-text amount-error asset-error to to-name sufficient-funds? symbol]} transaction + (let [{:keys [from amount amount-text amount-error asset-error to to-name sufficient-funds? symbol]} transaction {:keys [decimals] :as token} (tokens/asset-for all-tokens chain symbol) online? (= :online network-status)] [wallet.components/simple-screen {:avoid-keyboard? true @@ -54,6 +54,7 @@ :name to-name}] [wallet.components/asset-selector {:error asset-error + :address from :type :send :symbol symbol}] [wallet.components/amount-selector diff --git a/src/status_im/ui/screens/wallet/transactions/styles.cljs b/src/status_im/ui/screens/wallet/transactions/styles.cljs index 507af45447..cf7a000eba 100644 --- a/src/status_im/ui/screens/wallet/transactions/styles.cljs +++ b/src/status_im/ui/screens/wallet/transactions/styles.cljs @@ -34,6 +34,7 @@ (def empty-text {:text-align :center + :color colors/gray :margin-top 22 :margin-horizontal 92}) diff --git a/src/status_im/ui/screens/wallet/transactions/views.cljs b/src/status_im/ui/screens/wallet/transactions/views.cljs index 9cf80bd00d..5d3f038003 100644 --- a/src/status_im/ui/screens/wallet/transactions/views.cljs +++ b/src/status_im/ui/screens/wallet/transactions/views.cljs @@ -11,17 +11,14 @@ [status-im.ui.screens.wallet.transactions.styles :as styles]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) -;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; TRANSACTION HISTORY -;;;;;;;;;;;;;;;;;;;;;;;;;;; - (defn history-action [all-filters?] - (cond-> - {:icon :main-icons/filter - :icon-opts {:accessibility-label :filters-button} - :handler #(re-frame/dispatch [:navigate-to :wallet-transactions-filter])} - (not all-filters?) (assoc-in [:icon-opts :overlay-style] styles/corner-dot))) + (cond-> {:icon :main-icons/filter + :icon-opts {:accessibility-label :filters-button} + :handler #(re-frame/dispatch [:navigate-to :wallet-transactions-filter])} + + (not all-filters?) + (assoc-in [:icon-opts :overlay-style] styles/corner-dot))) (defn- toolbar-view [all-filters?] @@ -102,19 +99,6 @@ :key :transactions-history-empty}] :refreshing false}]]) -(defview transactions - [] - (letsubs [{:keys [transaction-history-sections all-filters?]} - [:wallet.transactions.history/screen]] - [react/view styles/transactions-view - [status-bar/status-bar] - [toolbar-view all-filters?] - [history-list transaction-history-sections]])) - -;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; TRANSACTION FILTERS -;;;;;;;;;;;;;;;;;;;;;;;;;;; - (defn- render-item-filter [{:keys [id label checked? on-touch]}] [react/view {:accessibility-label :filter-item} [list/list-item-with-checkbox @@ -151,10 +135,6 @@ :data filters}] :key-fn :id}]]])) -;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; TRANSACTION DETAILS -;;;;;;;;;;;;;;;;;;;;;;;;;;; - (defn details-header [date type amount-text currency-text] [react/view {:style styles/details-header} @@ -237,11 +217,11 @@ {:label (i18n/label :t/open-on-etherscan) :action #(.openURL (react/linking) url)}])]) -(defview transaction-details [] - (letsubs [{:keys [hash url type confirmations confirmations-progress +(defview transaction-details-view [hash address] + (letsubs [{:keys [url type confirmations confirmations-progress date amount-text currency-text] :as transaction} - [:wallet.transactions.details/screen]] + [:wallet.transactions.details/screen hash address]] [react/view {:style components.styles/flex} [status-bar/status-bar] [toolbar/toolbar {} @@ -253,3 +233,8 @@ [details-confirmations confirmations confirmations-progress (= :failed type)] [react/view {:style styles/details-separator}] [details-list transaction]]])) + +(defview transaction-details [] + (letsubs [{:keys [hash address]} [:get-screen-params]] + (when (and hash address) + [transaction-details-view hash address]))) \ No newline at end of file diff --git a/src/status_im/wallet/accounts/core.cljs b/src/status_im/wallet/accounts/core.cljs index 52fb8f000f..0e6d6b1965 100644 --- a/src/status_im/wallet/accounts/core.cljs +++ b/src/status_im/wallet/accounts/core.cljs @@ -4,14 +4,67 @@ [status-im.utils.fx :as fx] [status-im.ethereum.eip55 :as eip55] [status-im.ui.components.list-selection :as list-selection] - [status-im.utils.handlers :as handlers])) + [status-im.utils.handlers :as handlers] + [status-im.native-module.core :as status] + [status-im.utils.types :as types] + [status-im.constants :as constants] + [status-im.ui.components.colors :as colors] + [status-im.ui.screens.navigation :as navigation] + [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.wallet.core :as wallet] + [status-im.i18n :as i18n])) (re-frame/reg-fx :list.selection/open-share (fn [obj] (list-selection/open-share obj))) +(re-frame/reg-fx + :wallet.accounts/generate-account + (fn [{:keys [address password path-num]}] + (status/multiaccount-load-account + address + password + (fn [value] + (let [{:keys [id error]} (types/json->clj value)] + (if error + (re-frame/dispatch [:set-in [:generate-account :error] (i18n/label :t/add-account-incorrect-password)]) + (let [path (str constants/path-root "/" path-num)] + (status/multiaccount-derive-addresses + id + [path] + #(re-frame/dispatch [:wallet.accounts/account-generated + (merge + (get (types/json->clj %) (keyword path)) + {:name (str "Account " path-num) + :color (rand-nth colors/account-colors)})]))))))))) + (fx/defn set-symbol-request {:events [:wallet.accounts/share]} - [{:keys [db]}] - {:list.selection/open-share {:message (eip55/address->checksum (ethereum/default-address db))}}) \ No newline at end of file + [_ address] + {:list.selection/open-share {:message (eip55/address->checksum address)}}) + +(fx/defn generate-new-account + {:events [:wallet.accounts/generate-new-account]} + [{:keys [db]} password] + {:wallet.accounts/generate-account {:address (get-in db [:multiaccount :address]) + :path-num (inc (get-in db [:multiaccount :latest-derived-path])) + :password password}}) + +(fx/defn account-generated + {:events [:wallet.accounts/account-generated]} + [{:keys [db] :as cofx} account] + (fx/merge cofx + {:db (assoc db :generate-account {:account account})} + (navigation/navigate-to-cofx :account-added nil))) + +(fx/defn save-account + {:events [:wallet.accounts/save-generated-account]} + [{:keys [db] :as cofx}] + (let [new-account (get-in db [:generate-account :account]) + {:keys [accounts latest-derived-path]} (:multiaccount db)] + (fx/merge cofx + (multiaccounts.update/multiaccount-update {:accounts (conj accounts new-account) + :latest-derived-path (inc latest-derived-path)} nil) + (wallet/update-balances nil) + (navigation/navigate-to-cofx :wallet nil)))) \ No newline at end of file diff --git a/src/status_im/wallet/core.cljs b/src/status_im/wallet/core.cljs index c9277fcb73..49242db9b6 100644 --- a/src/status_im/wallet/core.cljs +++ b/src/status_im/wallet/core.cljs @@ -29,6 +29,16 @@ :on-success on-success :on-error on-error}))) +(re-frame/reg-fx + :wallet/get-balances + (fn [addresses] + (doseq [address addresses] + (json-rpc/call + {:method "eth_getBalance" + :params [address "latest"] + :on-success #(re-frame/dispatch [:wallet.callback/update-balance-success address %]) + :on-error #(re-frame/dispatch [:wallet.callback/update-balance-fail %])})))) + ;; TODO(oskarth): At some point we want to get list of relevant ;; assets to get prices for (re-frame/reg-fx @@ -55,21 +65,17 @@ [{:keys [db]} err] (log/debug "Unable to get balance: " err) {:db (-> db - (assoc-error-message :balance-update :error-unable-to-get-balance) - (assoc-in [:wallet :balance-loading?] false))}) + (assoc-error-message :balance-update :error-unable-to-get-balance))}) (fx/defn on-update-token-balance-fail [{:keys [db]} symbol err] (log/debug "Unable to get token " symbol "balance: " err) {:db (-> db - (assoc-error-message :balance-update :error-unable-to-get-token-balance) - (assoc-in [:wallet :balance-loading?] false))}) + (assoc-error-message :balance-update :error-unable-to-get-token-balance))}) (fx/defn open-transaction-details - [{:keys [db] :as cofx} hash] - (fx/merge cofx - {:db (assoc-in db [:wallet :current-transaction] hash)} - (navigation/navigate-to-cofx :wallet-transaction-details nil))) + [{:keys [db] :as cofx} hash address] + (navigation/navigate-to-cofx cofx :wallet-transaction-details {:hash hash :address address})) (defn- validate-token-name! [{:keys [address symbol name]}] @@ -136,18 +142,23 @@ (validate-token-name! token)))) (re-frame/reg-fx - :wallet/get-tokens-balance - (fn [{:keys [account-address tokens on-success on-error]}] + :wallet/get-tokens-balances + (fn [{:keys [addresses tokens assets]}] + ;;TODO not great to have so many calls , should be optimized, there is wallet_getTokensBalances why wouldn't use it? (doseq [{:keys [address symbol]} tokens] - (json-rpc/eth-call - {:contract address - :method "balanceOf(address)" - :params [account-address] - :outputs ["uint256"] - :on-success - (fn [[balance]] - (on-success symbol (money/bignumber balance))) - :on-error #(on-error symbol %)})))) + (doseq [account-address addresses] + (json-rpc/eth-call + {:contract address + :method "balanceOf(address)" + :params [account-address] + :outputs ["uint256"] + :on-success (fn [[balance]] + (if (and assets (assets symbol)) + (re-frame/dispatch [:wallet.callback/update-token-balance-success account-address symbol balance]) + ;; NOTE: when there it is not a visible assets we make an initialization round + (when (pos? balance) + (re-frame/dispatch [:wallet/token-found account-address symbol balance])))) + :on-error #(re-frame/dispatch [:wallet.callback/update-token-balance-fail symbol %])}))))) (defn clear-error-message [db error-type] (update-in db [:wallet :errors] dissoc error-type)) @@ -175,46 +186,22 @@ {:wallet/validate-tokens (get tokens/all-default-tokens chain)}))))) (fx/defn update-balances - [{{:keys [network-status :wallet/all-tokens] - {:keys [settings]} :multiaccount :as db} :db :as cofx}] - (let [normalized-address (ethereum/current-address db) - chain (ethereum/chain-keyword db) - assets (get-in settings [:wallet :visible-tokens chain]) - tokens (->> (tokens/tokens-for all-tokens chain) - (remove #(or (:hidden? %))))] + [{{:keys [network-status :wallet/all-tokens] + {:keys [settings accounts]} :multiaccount :as db} :db :as cofx} addresses] + (let [addresses (or addresses (map :address accounts)) + chain (ethereum/chain-keyword db) + assets (get-in settings [:wallet :visible-tokens chain]) + tokens (->> (tokens/tokens-for all-tokens chain) + (remove #(or (:hidden? %))))] (when (not= network-status :offline) (fx/merge cofx - {:wallet/get-balance - {:account-address normalized-address - :on-success #(re-frame/dispatch - [:wallet.callback/update-balance-success %]) - :on-error #(re-frame/dispatch - [:wallet.callback/update-balance-fail %])} - - :wallet/get-tokens-balance - {:account-address normalized-address - :tokens tokens - :on-success - (fn [symbol balance] - (if (and assets - (assets symbol)) - (re-frame/dispatch - [:wallet.callback/update-token-balance-success symbol balance]) - ;; NOTE: when there it is not a visible assets - ;; we make an initialization round - (when (> balance 0) - (re-frame/dispatch - [:wallet/token-found symbol balance])))) - :on-error - (fn [symbol error] - (re-frame/dispatch - [:wallet.callback/update-token-balance-fail symbol error]))} - - :db - (-> db - (clear-error-message :balance-update) - (assoc-in [:wallet :balance-loading?] true))} + {:wallet/get-balances addresses + :wallet/get-tokens-balances {:addresses addresses + :assets assets + :tokens tokens} + :db (-> db + (clear-error-message :balance-update))} (when-not assets (multiaccounts.update/update-settings (assoc-in settings @@ -260,16 +247,14 @@ :prices-loading? false)}) (fx/defn update-balance - [{:keys [db]} balance] + [{:keys [db]} address balance] {:db (-> db - (assoc-in [:wallet :balance :ETH] (money/bignumber balance)) - (assoc-in [:wallet :balance-loading?] false))}) + (assoc-in [:wallet :accounts address :balance :ETH] (money/bignumber balance)))}) (fx/defn update-token-balance - [{:keys [db]} symbol balance] + [{:keys [db]} address symbol balance] {:db (-> db - (assoc-in [:wallet :balance symbol] (money/bignumber balance)) - (assoc-in [:wallet :balance-loading?] false))}) + (assoc-in [:wallet :accounts address :balance symbol] (money/bignumber balance)))}) (defn update-toggle-in-settings [{{:keys [multiaccount] :as db} :db} symbol checked?] @@ -297,11 +282,11 @@ (multiaccounts.update/update-settings cofx new-settings {}))) (fx/defn configure-token-balance-and-visibility - [cofx symbol balance] + [cofx address symbol balance] (fx/merge cofx (toggle-visible-token symbol true) ;;TODO(goranjovic): move `update-token-balance-success` function to wallet models - (update-token-balance symbol balance))) + (update-token-balance address symbol balance))) (defn set-and-validate-amount-db [db amount symbol decimals] (let [{:keys [value error]} (wallet.db/parse-amount amount decimals)] diff --git a/src/status_im/wallet/custom_tokens/core.cljs b/src/status_im/wallet/custom_tokens/core.cljs index a367510106..c5008646f1 100644 --- a/src/status_im/wallet/custom_tokens/core.cljs +++ b/src/status_im/wallet/custom_tokens/core.cljs @@ -91,8 +91,7 @@ (fx/defn total-supply-result [{:keys [db]} contract total-supply] (if (money/valid? total-supply) - {:wallet.custom-token/get-balance - [contract (ethereum/default-address db)]} + {:wallet.custom-token/get-name contract} {:db (update db :wallet/custom-token-screen merge {:in-progress? nil diff --git a/src/status_im/wallet/db.cljs b/src/status_im/wallet/db.cljs index f692a84af8..49014f2243 100644 --- a/src/status_im/wallet/db.cljs +++ b/src/status_im/wallet/db.cljs @@ -66,8 +66,7 @@ #{:inbound :outbound :pending :failed}) (def default-wallet - {:filters default-wallet-filters - :transactions empty-transaction-map}) + {:filters default-wallet-filters}) (defn get-confirmations [{:keys [block]} current-block] diff --git a/test/cljs/status_im/test/signing/core.cljs b/test/cljs/status_im/test/signing/core.cljs index 31eb286382..997e0e9e72 100644 --- a/test/cljs/status_im/test/signing/core.cljs +++ b/test/cljs/status_im/test/signing/core.cljs @@ -26,7 +26,8 @@ (testing "first tx object is parsed" (is (= (dissoc (get-in sign-first [:db :signing/tx]) :token) (merge first-tx - {:gas nil + {:from {:address nil} + :gas nil :gasPrice nil :data nil :to to @@ -49,7 +50,8 @@ (testing "second tx object is parsed" (is (= (dissoc (get-in first-discarded [:db :signing/tx]) :token) (merge second-tx - {:gas nil + {:from {:address nil} + :gas nil :gasPrice nil :data data :to contract diff --git a/translations/en.json b/translations/en.json index 31582d2361..39e86886d1 100644 --- a/translations/en.json +++ b/translations/en.json @@ -173,7 +173,7 @@ "intro-wizard-text7": "Status will notify you about new messages. You can edit your notification preferences later in settings", "generate-a-key": "Generate a key", "generate-a-new-key": "Generate a new key", - "enter-your-password": "Enter your password...", + "enter-your-password": "Enter your password", "generating-keys": "Generating keys...", "you-will-need-this-code": "You'll need this code to open Status and sign transactions", "this-device": "This device", @@ -1259,5 +1259,15 @@ "not-keycard-title": "Not a Keycard", "not-keycard-text": "The card you used is not a Keycard. You need to purchase a Keycard to use it", "create-new-key": "Create a new key", - "add-another-key": "Add another key" + "add-another-key": "Add another key", + "generate-a-new-account": "Generate a new account", + "generate-account": "Generate account", + "add-account-description": "You can import any type of Ethereum account to add it to your Status wallet", + "account-added": "Account added", + "you-can-change-account": "You can change the account name and color to what you wish", + "account-name": "Account name", + "account-color": "Account color", + "to-encrypt-enter-password": "To encrypt the account please enter your password", + "accounts": "Accounts", + "add-account-incorrect-password": "Password seems to be incorrect. Enter the password you use to unlock the app." }