From 9c7cb0fe93f0ac83213eb3de505de217b2ee13a8 Mon Sep 17 00:00:00 2001 From: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com> Date: Fri, 7 Jun 2024 20:28:49 +0530 Subject: [PATCH] feat(wallet)!: process wallet accounts from backup on account recovery (#20160) This commit: - adds a feature to process backed-up wallet data on account recovery (without the necessity to re-login) - refactors keypair data store functions - refactors wallet event to support calling for single account/address Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com> --- src/status_im/common/signals/events.cljs | 6 + .../wallet/account/tabs/assets/view.cljs | 2 +- .../add_account/create_account/events.cljs | 4 +- .../create_account/select_keypair/view.cljs | 15 +- .../contexts/wallet/collectible/events.cljs | 14 ++ .../wallet/collectible/events_test.cljs | 70 ++++++++ src/status_im/contexts/wallet/data_store.cljs | 62 +++---- src/status_im/contexts/wallet/db.cljs | 3 +- src/status_im/contexts/wallet/events.cljs | 160 ++++++++++++------ .../contexts/wallet/events_test.cljs | 148 ++++++++++------ .../wallet/home/tabs/assets/view.cljs | 2 +- src/status_im/contexts/wallet/home/view.cljs | 2 +- src/status_im/contexts/wallet/signals.cljs | 4 +- src/status_im/subs/wallet/wallet.cljs | 34 +++- src/status_im/subs/wallet/wallet_test.cljs | 84 +++++---- src/test_helpers/integration.cljs | 2 +- 16 files changed, 402 insertions(+), 210 deletions(-) create mode 100644 src/status_im/contexts/wallet/collectible/events_test.cljs diff --git a/src/status_im/common/signals/events.cljs b/src/status_im/common/signals/events.cljs index 2907134e05..e64753a87f 100644 --- a/src/status_im/common/signals/events.cljs +++ b/src/status_im/common/signals/events.cljs @@ -105,6 +105,12 @@ "waku.backedup.settings" {:fx [[:dispatch [:profile/update-setting-from-backup (transforms/js->clj event-js)]]]} + "waku.backedup.keypair" + {:fx [[:dispatch [:wallet/process-keypair-from-backup (transforms/js->clj event-js)]]]} + + "waku.backedup.watch-only-account" + {:fx [[:dispatch [:wallet/process-watch-only-account-from-backup (transforms/js->clj event-js)]]]} + "mediaserver.started" {:db (assoc db :mediaserver/port (oops/oget event-js :port))} diff --git a/src/status_im/contexts/wallet/account/tabs/assets/view.cljs b/src/status_im/contexts/wallet/account/tabs/assets/view.cljs index 609f3c2a2c..23c315690b 100644 --- a/src/status_im/contexts/wallet/account/tabs/assets/view.cljs +++ b/src/status_im/contexts/wallet/account/tabs/assets/view.cljs @@ -8,7 +8,7 @@ (defn view [] - (let [tokens-loading? (rf/sub [:wallet/tokens-loading?]) + (let [tokens-loading? (rf/sub [:wallet/current-viewing-account-tokens-loading?]) tokens (rf/sub [:wallet/current-viewing-account-token-values]) {:keys [watch-only?]} (rf/sub [:wallet/current-viewing-account])] (if tokens-loading? diff --git a/src/status_im/contexts/wallet/add_account/create_account/events.cljs b/src/status_im/contexts/wallet/add_account/create_account/events.cljs index e8e9e83f74..c0694b5f96 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/events.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/events.cljs @@ -11,8 +11,8 @@ (defn get-keypairs-success [{:keys [db]} [keypairs]] - (let [parsed-keypairs (data-store/parse-keypairs keypairs) - default-key-uid (:key-uid (first parsed-keypairs))] + (let [parsed-keypairs (data-store/rpc->keypairs keypairs) + default-key-uid (:key-uid (some #(when (= (:type %) :profile) %) parsed-keypairs))] {:db (-> db (assoc-in [:wallet :keypairs] parsed-keypairs) (assoc-in [:wallet :ui :create-account :selected-keypair-uid] default-key-uid))})) diff --git a/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs b/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs index 4130dc7abc..dcf2331e5e 100644 --- a/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs +++ b/src/status_im/contexts/wallet/add_account/create_account/select_keypair/view.cljs @@ -34,8 +34,8 @@ (->> given-accounts (filter (fn [{:keys [path]}] (not (string/starts-with? path constants/path-eip1581)))) - (map (fn [{:keys [customization-color emoji name address]}] - {:account-props {:customization-color customization-color + (map (fn [{:keys [color emoji name address]}] + {:account-props {:customization-color color :size 32 :emoji emoji :type :default @@ -48,20 +48,21 @@ :action :none})))) (defn- keypair - [item index _ + [item _ _ {:keys [profile-picture compressed-key selected-key-uid set-selected-key-uid customization-color]}] - (let [accounts (parse-accounts (:accounts item))] + (let [profile-keypair? (= (:type item) :profile) + accounts (parse-accounts (:accounts item))] [quo/keypair {:customization-color customization-color - :profile-picture (when (zero? index) profile-picture) + :profile-picture (when profile-keypair? profile-picture) :status-indicator false - :type (if (zero? index) :default-keypair :other) + :type (if profile-keypair? :default-keypair :other) :stored :on-device :on-options-press #(js/alert "Options pressed") :action :selector :blur? false :details {:full-name (:name item) - :address (when (zero? index) + :address (when profile-keypair? (utils/get-shortened-compressed-key compressed-key))} :on-press #(set-selected-key-uid (:key-uid item)) :accounts accounts diff --git a/src/status_im/contexts/wallet/collectible/events.cljs b/src/status_im/contexts/wallet/collectible/events.cljs index b4252844fe..2c5a898fc4 100644 --- a/src/status_im/contexts/wallet/collectible/events.cljs +++ b/src/status_im/contexts/wallet/collectible/events.cljs @@ -110,6 +110,20 @@ new-request? (update-in [:wallet :accounts] update-vals #(dissoc % :collectibles))) :fx collectible-requests}))) +(defn request-new-collectibles-for-account-from-signal + [{:keys [db]} [address]] + (let [pending-requests (get-in db [:wallet :ui :collectibles :pending-requests] 0) + [request-id] (get-unique-collectible-request-id 1)] + {:db (assoc-in db [:wallet :ui :collectibles :pending-requests] (inc pending-requests)) + :fx [[:dispatch + [:wallet/request-new-collectibles-for-account + {:request-id request-id + :account address + :amount collectibles-request-batch-size}]]]})) + +(rf/reg-event-fx :wallet/request-new-collectibles-for-account-from-signal + request-new-collectibles-for-account-from-signal) + (rf/reg-event-fx :wallet/request-collectibles-for-current-viewing-account (fn [{:keys [db]} _] diff --git a/src/status_im/contexts/wallet/collectible/events_test.cljs b/src/status_im/contexts/wallet/collectible/events_test.cljs new file mode 100644 index 0000000000..adb0910768 --- /dev/null +++ b/src/status_im/contexts/wallet/collectible/events_test.cljs @@ -0,0 +1,70 @@ +(ns status-im.contexts.wallet.collectible.events-test + (:require + [cljs.test :refer-macros [deftest is testing]] + matcher-combinators.test + [status-im.contexts.wallet.collectible.events :as events])) + +(deftest store-collectibles-test + (testing "flush-collectibles" + (let [collectible-1 {:collectible-data {:image-url "https://..." :animation-url "https://..."} + :ownership [{:address "0x1" + :balance "1"}]} + collectible-2 {:collectible-data {:image-url "" :animation-url "https://..."} + :ownership [{:address "0x1" + :balance "1"}]} + collectible-3 {:collectible-data {:image-url "" :animation-url nil} + :ownership [{:address "0x2" + :balance "1"}]} + db {:wallet {:ui {:collectibles {:pending-requests 0 + :fetched {"0x1" [collectible-1 + collectible-2] + "0x2" [collectible-3]}}} + :accounts {"0x1" {} + "0x3" {}}}} + expected-db {:wallet {:ui {:collectibles {}} + :accounts {"0x1" {:collectibles (list collectible-1 collectible-2)} + "0x2" {:collectibles (list collectible-3)} + "0x3" {}}}} + result-db (:db (events/flush-collectibles {:db db}))] + + (is (match? result-db expected-db))))) + +(deftest clear-stored-collectibles-test + (let [db {:wallet {:accounts {"0x1" {:collectibles [{:id 1} {:id 2}]} + "0x2" {"some other stuff" "with any value" + :collectibles [{:id 3}]} + "0x3" {}}}}] + (testing "clear-stored-collectibles" + (let [expected-db {:wallet {:accounts {"0x1" {} + "0x2" {"some other stuff" "with any value"} + "0x3" {}}}} + effects (events/clear-stored-collectibles {:db db}) + result-db (:db effects)] + + (is (match? result-db expected-db)))))) + +(deftest store-last-collectible-details-test + (testing "store-last-collectible-details" + (let [db {:wallet {}} + last-collectible {:description "Pandaria" + :image-url "https://..."} + expected-db {:wallet {:last-collectible-details {:description "Pandaria" + :image-url "https://..."}}} + effects (events/store-last-collectible-details {:db db} + [last-collectible]) + result-db (:db effects)] + (is (match? result-db expected-db))))) + +(deftest request-new-collectibles-for-account-from-signal-test + (testing "request new collectibles for account from signal" + (let [db {:wallet {}} + address "0x1" + expected {:db {:wallet {:ui {:collectibles {:pending-requests 1}}}} + :fx [[:dispatch + [:wallet/request-new-collectibles-for-account + {:request-id 0 + :account address + :amount events/collectibles-request-batch-size}]]]} + effects (events/request-new-collectibles-for-account-from-signal {:db db} + [address])] + (is (match? expected effects))))) diff --git a/src/status_im/contexts/wallet/data_store.cljs b/src/status_im/contexts/wallet/data_store.cljs index 5a0fe2d3db..c84ba7feb9 100644 --- a/src/status_im/contexts/wallet/data_store.cljs +++ b/src/status_im/contexts/wallet/data_store.cljs @@ -1,6 +1,5 @@ (ns status-im.contexts.wallet.data-store (:require - [camel-snake-kebab.core :as csk] [camel-snake-kebab.extras :as cske] [clojure.set :as set] [clojure.string :as string] @@ -22,7 +21,9 @@ (defn add-keys-to-account [account] - (assoc account :watch-only? (= (:type account) :watch))) + (-> account + (assoc :watch-only? (= (:type account) :watch)) + (assoc :default-account? (:wallet account)))) (defn- sanitize-emoji "As Desktop uses Twemoji, the emoji received can be an img tag @@ -44,9 +45,11 @@ (update :test-preferred-chain-ids chain-ids-string->set) (update :type keyword) (update :operable keyword) - (update :color #(if (seq %) (keyword %) constants/account-default-customization-color)) + (update :color + #(if (and (not (keyword? %)) (string/blank? %)) + constants/account-default-customization-color + (keyword %))) (update :emoji sanitize-emoji) - (assoc :default-account? (:wallet account)) add-keys-to-account)) (defn rpc->accounts @@ -107,36 +110,33 @@ :nativeCurrencySymbol :native-currency-symbol :nativeCurrencyName :native-currency-symbol}))) -(defn sort-keypairs - [keypairs] - (sort-by #(if (some (fn [account] - (string/starts-with? (:path account) constants/path-eip1581)) - (:accounts %)) - 0 - 1) - keypairs)) +(defn get-keypair-lowest-operability + [{:keys [accounts]}] + (cond + (some #(= (:operable %) :no) accounts) + :no -(defn sort-and-rename-keypairs - [keypairs] - (let [sorted-keypairs (sort-keypairs keypairs)] - (map (fn [item] - (update item - :accounts - (fn [accounts] - (map - (fn [{:keys [colorId] :as account}] - (assoc account - :customization-color - (if (seq colorId) - (keyword colorId) - :blue))) - accounts)))) - sorted-keypairs))) + (some #(= (:operable %) :partially) accounts) + :partially -(defn parse-keypairs + :else + :fully)) + +(defn- add-keys-to-keypair + [keypair] + (assoc keypair :lowest-operability (get-keypair-lowest-operability keypair))) + +(defn rpc->keypair + [keypair] + (-> keypair + (update :type keyword) + (update :accounts #(map rpc->account %)) + add-keys-to-keypair)) + +(defn rpc->keypairs [keypairs] - (let [renamed-data (sort-and-rename-keypairs keypairs)] - (cske/transform-keys csk/->kebab-case-keyword renamed-data))) + (->> (map rpc->keypair keypairs) + (sort-by #(if (= (:type %) :profile) 0 1)))) (defn- add-keys-to-saved-address [saved-address] diff --git a/src/status_im/contexts/wallet/db.cljs b/src/status_im/contexts/wallet/db.cljs index 17b8aa9dd0..6d98804f13 100644 --- a/src/status_im/contexts/wallet/db.cljs +++ b/src/status_im/contexts/wallet/db.cljs @@ -6,5 +6,4 @@ :selected-networks (set constants/default-network-names)}) (def defaults - {:ui {:network-filter network-filter-defaults - :tokens-loading? true}}) + {:ui {:network-filter network-filter-defaults}}) diff --git a/src/status_im/contexts/wallet/events.cljs b/src/status_im/contexts/wallet/events.cljs index 0696bc7cf1..37c510bd54 100644 --- a/src/status_im/contexts/wallet/events.cljs +++ b/src/status_im/contexts/wallet/events.cljs @@ -57,19 +57,27 @@ {:fx [[:dispatch [:wallet/clean-current-viewing-account]] [:dispatch [:pop-to-root :shell-stack]]]})) +(defn log-rpc-error + [_ [{:keys [event params]} error]] + (log/warn (str "[wallet] Failed to " event) + {:params params + :error error})) + +(rf/reg-event-fx :wallet/log-rpc-error log-rpc-error) + (rf/reg-event-fx :wallet/get-accounts-success (fn [{:keys [db]} [accounts]] - (let [wallet-accounts (filter #(not (:chat %)) accounts) + (let [wallet-accounts (data-store/rpc->accounts accounts) wallet-db (get db :wallet) new-account? (:new-account? wallet-db) navigate-to-account (:navigate-to-account wallet-db)] {:db (assoc-in db [:wallet :accounts] - (utils.collection/index-by :address (data-store/rpc->accounts wallet-accounts))) - :fx [[:dispatch [:wallet/get-wallet-token]] + (utils.collection/index-by :address wallet-accounts)) + :fx [[:dispatch [:wallet/get-wallet-token-for-all-accounts]] [:dispatch [:wallet/request-collectibles-for-all-accounts {:new-request? true}]] - [:dispatch [:wallet/check-recent-history]] + [:dispatch [:wallet/check-recent-history-for-all-accounts]] (when new-account? [:dispatch [:wallet/navigate-to-new-account navigate-to-account]])]}))) @@ -79,9 +87,16 @@ {:fx [[:json-rpc/call [{:method "accounts_getAccounts" :on-success [:wallet/get-accounts-success] - :on-error #(log/info "failed to get accounts " - {:error % - :event :wallet/get-accounts})}]]]})) + :on-error [:wallet/log-rpc-error {:event :wallet/get-accounts}]}]]]})) + +(defn process-account-from-signal + [{:keys [db]} [{:keys [address] :as account}]] + {:db (assoc-in db [:wallet :accounts address] (data-store/rpc->account account)) + :fx [[:dispatch [:wallet/get-wallet-token-for-account address]] + [:dispatch [:wallet/request-new-collectibles-for-account-from-signal address]] + [:dispatch [:wallet/check-recent-history-for-account address]]]}) + +(rf/reg-event-fx :wallet/process-account-from-signal process-account-from-signal) (rf/reg-event-fx :wallet/save-account @@ -93,9 +108,7 @@ (rf/dispatch [:wallet/get-accounts]) (when (fn? on-success) (on-success))) - :on-error #(log/info "failed to save account " - {:error % - :event :wallet/save-account})}]]]})) + :on-error [:wallet/log-rpc-error {:event :wallet/save-account}]}]]]})) (rf/reg-event-fx :wallet/show-account-deleted-toast @@ -125,35 +138,41 @@ [{:method "accounts_deleteAccount" :params [address] :on-success [:wallet/remove-account-success toast-message] - :on-error #(log/info "failed to remove account " - {:error % - :event :wallet/remove-account})}]]]})) + :on-error [:wallet/log-rpc-error {:event :wallet/remove-account}]}]]]})) + +(defn get-wallet-token-for-all-accounts + [{:keys [db]}] + {:fx (->> (get-in db [:wallet :accounts]) + vals + (mapv + (fn [{:keys [address]}] + [:dispatch [:wallet/get-wallet-token-for-account address]])))}) + +(rf/reg-event-fx :wallet/get-wallet-token-for-all-accounts get-wallet-token-for-all-accounts) + +(defn get-wallet-token-for-account + [{:keys [db]} [address]] + {:db (assoc-in db [:wallet :ui :tokens-loading address] true) + :fx [[:json-rpc/call + [{:method "wallet_getWalletToken" + :params [[address]] + :on-success [:wallet/store-wallet-token address] + :on-error [:wallet/get-wallet-token-for-account-failed address]}]]]}) + +(rf/reg-event-fx :wallet/get-wallet-token-for-account get-wallet-token-for-account) (rf/reg-event-fx - :wallet/get-wallet-token - (fn [{:keys [db]}] - (let [addresses (->> (get-in db [:wallet :accounts]) - vals - (map :address))] - {:db (assoc-in db [:wallet :ui :tokens-loading?] true) - :fx [[:json-rpc/call - [{:method "wallet_getWalletToken" - :params [addresses] - :on-success [:wallet/store-wallet-token] - :on-error [:wallet/get-wallet-token-failed addresses]}]]]}))) - -(rf/reg-event-fx - :wallet/get-wallet-token-failed - (fn [{:keys [db]} [params error]] + :wallet/get-wallet-token-for-account-failed + (fn [{:keys [db]} [address error]] (log/info "failed to get wallet token " {:error error - :event :wallet/get-wallet-token - :params params}) - {:db (assoc-in db [:wallet :ui :tokens-loading?] false)})) + :event :wallet/get-wallet-token-for-account + :params address}) + {:db (assoc-in db [:wallet :ui :tokens-loading address] false)})) (rf/reg-event-fx :wallet/store-wallet-token - (fn [{:keys [db]} [raw-tokens-data]] + (fn [{:keys [db]} [address raw-tokens-data]] (let [tokens (data-store/rpc->tokens raw-tokens-data) add-tokens (fn [stored-accounts tokens-per-account] (reduce-kv (fn [accounts address tokens-data] @@ -164,7 +183,7 @@ tokens-per-account))] {:db (-> db (update-in [:wallet :accounts] add-tokens tokens) - (assoc-in [:wallet :ui :tokens-loading?] false))}))) + (assoc-in [:wallet :ui :tokens-loading address] false))}))) (rf/defn scan-address-success {:events [:wallet/scan-address-success]} @@ -209,7 +228,9 @@ (security/safe-unmask-data password)) account-config] :on-success [:wallet/add-account-success lowercase-address] - :on-error #(log/info "failed to create account " % account-config)}]]]}))) + :on-error [:wallet/log-rpc-error + {:event :wallet/add-account + :params account-config}]}]]]}))) (defn get-keypairs [_] @@ -217,7 +238,7 @@ [{:method "accounts_getKeypairs" :params [] :on-success [:wallet/get-keypairs-success] - :on-error #(log/info "failed to get keypairs " %)}]]]}) + :on-error [:wallet/log-rpc-error {:event :wallet/get-keypairs}]}]]]}) (rf/reg-event-fx :wallet/get-keypairs get-keypairs) @@ -254,7 +275,7 @@ [{:method "wallet_getEthereumChains" :params [] :on-success [:wallet/get-ethereum-chains-success] - :on-error #(log/info "failed to get networks " %)}]})) + :on-error [:wallet/log-rpc-error {:event :wallet/get-ethereum-chains}]}]})) (rf/reg-event-fx :wallet/get-ethereum-chains-success @@ -354,29 +375,34 @@ (rf/reg-event-fx :wallet/reload (fn [_] - {:fx [[:dispatch-n [[:wallet/get-wallet-token]]]]})) + {:fx [[:dispatch [:wallet/get-wallet-token-for-all-accounts]]]})) (rf/reg-event-fx :wallet/start-wallet (fn [_] {:fx [[:json-rpc/call [{:method "wallet_startWallet" - :on-error #(log/info "failed to start wallet" - {:error % - :event :wallet/start-wallet})}]]]})) + :on-error [:wallet/log-rpc-error {:event :wallet/start-wallet}]}]]]})) + +(defn check-recent-history-for-all-accounts + [{:keys [db]}] + {:fx (->> (get-in db [:wallet :accounts]) + vals + (mapv (fn [{:keys [address]}] + [:dispatch [:wallet/check-recent-history-for-account address]])))}) + +(rf/reg-event-fx :wallet/check-recent-history-for-all-accounts check-recent-history-for-all-accounts) (rf/reg-event-fx - :wallet/check-recent-history - (fn [{:keys [db]}] - (let [addresses (->> (get-in db [:wallet :accounts]) - vals - (map :address)) - chain-ids (chain/chain-ids db)] + :wallet/check-recent-history-for-account + (fn [{:keys [db]} [address]] + (let [chain-ids (chain/chain-ids db) + params [chain-ids [address]]] {:fx [[:json-rpc/call [{:method "wallet_checkRecentHistoryForChainIDs" - :params [chain-ids addresses] - :on-error #(log/info "failed to check recent history" - {:error % - :event :wallet/check-recent-history})}]]]}))) + :params params + :on-error [:wallet/log-rpc-error + {:event :wallet/check-recent-history-for-account + :params params}]}]]]}))) (rf/reg-event-fx :wallet/initialize (fn [] @@ -496,9 +522,9 @@ ;; https://github.com/status-im/status-mobile/issues/19864 :method "wallet_filterActivityAsync" :params request-params - :on-error #(log/info "failed to fetch activities" - {:error % - :event :wallet/fetch-activities})}]]]}))) + :on-error [:wallet/log-rpc-error + {:event :wallet/fetch-activities + :params request-params}]}]]]}))) (rf/reg-event-fx :wallet/activity-filtering-done @@ -523,9 +549,7 @@ {:fx [[:json-rpc/call [{:method "wallet_getCryptoOnRamps" :on-success [:wallet/get-crypto-on-ramps-success] - :on-error #(log/info "failed to fetch crypto on ramps" - {:error % - :event :wallet/get-crypto-on-ramps})}]]]})) + :on-error [:wallet/log-rpc-error {:event :wallet/get-crypto-on-ramps}]}]]]})) (rf/reg-event-fx :wallet/resolve-ens @@ -536,3 +560,27 @@ :params [chain-id ens] :on-success on-success :on-error on-error}]]]}))) + +(rf/reg-event-fx + :wallet/process-keypair-from-backup + (fn [{:keys [db]} [{:keys [backedUpKeypair]}]] + (let [{:keys [key-uid accounts]} backedUpKeypair + keypairs-db (get-in db [:wallet :keypairs]) + updated-keypairs (-> (filter #(not= (:key-uid %) key-uid) keypairs-db) + (conj backedUpKeypair) + data-store/rpc->keypairs) + accounts-fx (mapv (fn [{:keys [chat] :as account}] + ;; We exclude the chat account from the profile keypair + ;; for fetching the assets + (when-not chat + [:dispatch + [:wallet/process-account-from-signal + account]])) + accounts)] + {:db (assoc-in db [:wallet :keypairs] updated-keypairs) + :fx accounts-fx}))) + +(rf/reg-event-fx + :wallet/process-watch-only-account-from-backup + (fn [_ [{:keys [backedUpWatchOnlyAccount]}]] + {:fx [[:dispatch [:wallet/process-account-from-signal backedUpWatchOnlyAccount]]]})) diff --git a/src/status_im/contexts/wallet/events_test.cljs b/src/status_im/contexts/wallet/events_test.cljs index 80d2c60ccf..2bd0a090a7 100644 --- a/src/status_im/contexts/wallet/events_test.cljs +++ b/src/status_im/contexts/wallet/events_test.cljs @@ -3,10 +3,55 @@ [cljs.test :refer-macros [deftest is testing]] matcher-combinators.test [status-im.constants :as constants] - [status-im.contexts.wallet.collectible.events :as collectible-events] [status-im.contexts.wallet.events :as events])) -(def address "0x2f88d65f3cb52605a54a833ae118fb1363acccd2") +(def address "0x2ee6138eb9344a8b76eca3cf7554a06c82a1e2d8") + +(def raw-account + {:path "m/44'/60'/0'/0/0" + :emoji "🛃" + :key-uid "0xf9b4dc40911638052ef9cbed6e8ac689198d8f11d2235c5d62e2457c1503dc4f" + :address address + :wallet true + :name "Ethereum account" + :createdAt 1716548742000 + :type "generated" + :chat false + :prodPreferredChainIds "1:42161" + :hidden false + :position 0 + :clock 1712315009484 + :testPreferredChainIds "11155111:421614" + :colorId "purple" + :operable "fully" + :mixedcase-address "0x2Ee6138eb9344a8b76Eca3cf7554A06C82A1e2D8" + :public-key + "0x04ee7c47e4b68cc05dcd3377cbd5cde6be3c89fcf20a981e55e0285ed63a50f51f8b423465eee134c51bb0255e6041e9e5b006054b0fa72a7c76942a5a1a3f4e7e" + :removed false}) + +(def account + {:path "m/44'/60'/0'/0/0" + :emoji "🛃" + :key-uid "0xf9b4dc40911638052ef9cbed6e8ac689198d8f11d2235c5d62e2457c1503dc4f" + :address address + :color :purple + :wallet true + :default-account? true + :name "Ethereum account" + :type :generated + :chat false + :test-preferred-chain-ids #{11155111 421614} + :watch-only? false + :hidden false + :prod-preferred-chain-ids #{1 42161} + :position 0 + :clock 1712315009484 + :created-at 1716548742000 + :operable :fully + :mixedcase-address "0x2Ee6138eb9344a8b76Eca3cf7554A06C82A1e2D8" + :public-key + "0x04ee7c47e4b68cc05dcd3377cbd5cde6be3c89fcf20a981e55e0285ed63a50f51f8b423465eee134c51bb0255e6041e9e5b006054b0fa72a7c76942a5a1a3f4e7e" + :removed false}) (deftest scan-address-success-test (let [db {}] @@ -25,57 +70,6 @@ result-db (:db effects)] (is (match? result-db expected-db)))))) -(deftest store-collectibles-test - (testing "flush-collectibles" - (let [collectible-1 {:collectible-data {:image-url "https://..." :animation-url "https://..."} - :ownership [{:address "0x1" - :balance "1"}]} - collectible-2 {:collectible-data {:image-url "" :animation-url "https://..."} - :ownership [{:address "0x1" - :balance "1"}]} - collectible-3 {:collectible-data {:image-url "" :animation-url nil} - :ownership [{:address "0x2" - :balance "1"}]} - db {:wallet {:ui {:collectibles {:pending-requests 0 - :fetched {"0x1" [collectible-1 - collectible-2] - "0x2" [collectible-3]}}} - :accounts {"0x1" {} - "0x3" {}}}} - expected-db {:wallet {:ui {:collectibles {}} - :accounts {"0x1" {:collectibles (list collectible-1 collectible-2)} - "0x2" {:collectibles (list collectible-3)} - "0x3" {}}}} - result-db (:db (collectible-events/flush-collectibles {:db db}))] - - (is (match? result-db expected-db))))) - -(deftest clear-stored-collectibles-test - (let [db {:wallet {:accounts {"0x1" {:collectibles [{:id 1} {:id 2}]} - "0x2" {"some other stuff" "with any value" - :collectibles [{:id 3}]} - "0x3" {}}}}] - (testing "clear-stored-collectibles" - (let [expected-db {:wallet {:accounts {"0x1" {} - "0x2" {"some other stuff" "with any value"} - "0x3" {}}}} - effects (collectible-events/clear-stored-collectibles {:db db}) - result-db (:db effects)] - - (is (match? result-db expected-db)))))) - -(deftest store-last-collectible-details-test - (testing "store-last-collectible-details" - (let [db {:wallet {}} - last-collectible {:description "Pandaria" - :image-url "https://..."} - expected-db {:wallet {:last-collectible-details {:description "Pandaria" - :image-url "https://..."}}} - effects (collectible-events/store-last-collectible-details {:db db} - [last-collectible]) - result-db (:db effects)] - (is (match? result-db expected-db))))) - (deftest reset-selected-networks-test (testing "reset-selected-networks" (let [db {:wallet {}} @@ -122,3 +116,51 @@ effects (events/update-selected-networks {:db db} props) result-fx (:fx effects)] (is (match? result-fx expected-fx))))) + +(deftest get-wallet-token-for-all-accounts-test + (testing "get wallet token for all accounts" + (let [address-1 "0x1" + address-2 "0x2" + cofx {:db {:wallet {:accounts {address-1 {:address address-1} + address-2 {:address address-2}}}}} + effects (events/get-wallet-token-for-all-accounts cofx) + result-fx (:fx effects) + expected-fx [[:dispatch [:wallet/get-wallet-token-for-account address-1]] + [:dispatch [:wallet/get-wallet-token-for-account address-2]]]] + (is (match? expected-fx result-fx))))) + +(deftest get-wallet-token-for-account-test + (testing "get wallet token for account" + (let [cofx {:db {}} + effects (events/get-wallet-token-for-account cofx [address]) + expected-effects {:db {:wallet {:ui {:tokens-loading {address true}}}} + :fx [[:json-rpc/call + [{:method "wallet_getWalletToken" + :params [[address]] + :on-success [:wallet/store-wallet-token address] + :on-error [:wallet/get-wallet-token-for-account-failed + address]}]]]}] + (is (match? expected-effects effects))))) + +(deftest check-recent-history-for-all-accounts-test + (testing "check recent history for all accounts" + (let [address-1 "0x1" + address-2 "0x2" + cofx {:db {:wallet {:accounts {address-1 {:address address-1} + address-2 {:address address-2}}}}} + effects (events/check-recent-history-for-all-accounts cofx) + result-fx (:fx effects) + expected-fx [[:dispatch [:wallet/check-recent-history-for-account address-1]] + [:dispatch [:wallet/check-recent-history-for-account address-2]]]] + (is (match? expected-fx result-fx))))) + +(deftest process-account-from-signal-test + (testing "process account from signal" + (let [cofx {:db {:wallet {:accounts {}}}} + effects (events/process-account-from-signal cofx [raw-account]) + expected-effects {:db {:wallet {:accounts {address account}}} + :fx [[:dispatch [:wallet/get-wallet-token-for-account address]] + [:dispatch + [:wallet/request-new-collectibles-for-account-from-signal address]] + [:dispatch [:wallet/check-recent-history-for-account address]]]}] + (is (match? expected-effects effects))))) diff --git a/src/status_im/contexts/wallet/home/tabs/assets/view.cljs b/src/status_im/contexts/wallet/home/tabs/assets/view.cljs index 9366b2ce78..0fd82dacae 100644 --- a/src/status_im/contexts/wallet/home/tabs/assets/view.cljs +++ b/src/status_im/contexts/wallet/home/tabs/assets/view.cljs @@ -8,7 +8,7 @@ (defn view [] - (let [tokens-loading? (rf/sub [:wallet/tokens-loading?]) + (let [tokens-loading? (rf/sub [:wallet/home-tokens-loading?]) {:keys [tokens]} (rf/sub [:wallet/aggregated-token-values-and-balance])] (if tokens-loading? [quo/skeleton-list diff --git a/src/status_im/contexts/wallet/home/view.cljs b/src/status_im/contexts/wallet/home/view.cljs index 00426bf367..b28cd19653 100644 --- a/src/status_im/contexts/wallet/home/view.cljs +++ b/src/status_im/contexts/wallet/home/view.cljs @@ -65,7 +65,7 @@ [] (let [[selected-tab set-selected-tab] (rn/use-state (:id (first tabs-data))) account-list-ref (rn/use-ref-atom nil) - tokens-loading? (rf/sub [:wallet/tokens-loading?]) + tokens-loading? (rf/sub [:wallet/home-tokens-loading?]) networks (rf/sub [:wallet/selected-network-details]) account-cards-data (rf/sub [:wallet/account-cards-data]) cards (conj account-cards-data (new-account-card-data)) diff --git a/src/status_im/contexts/wallet/signals.cljs b/src/status_im/contexts/wallet/signals.cljs index c3a0d3aebe..04208e1f90 100644 --- a/src/status_im/contexts/wallet/signals.cljs +++ b/src/status_im/contexts/wallet/signals.cljs @@ -40,6 +40,4 @@ "wallet-activity-filtering-done" {:fx [[:dispatch [:wallet/activity-filtering-done (transforms/js->clj event-js)]]]} - (log/debug ::unknown-wallet-event - :type event-type - :event (transforms/js->clj event-js)))))) + (log/debug ::unknown-wallet-event :type event-type))))) diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs index bbd766083d..de15c26f9c 100644 --- a/src/status_im/subs/wallet/wallet.cljs +++ b/src/status_im/subs/wallet/wallet.cljs @@ -39,9 +39,25 @@ :-> :scanned-address) (rf/reg-sub - :wallet/tokens-loading? + :wallet/tokens-loading :<- [:wallet/ui] - :-> :tokens-loading?) + :-> :tokens-loading) + +(rf/reg-sub + :wallet/home-tokens-loading? + :<- [:wallet/tokens-loading] + (fn [tokens-loading] + (->> tokens-loading + vals + (some true?) + boolean))) + +(rf/reg-sub + :wallet/current-viewing-account-tokens-loading? + :<- [:wallet/tokens-loading] + :<- [:wallet/current-viewing-account-address] + (fn [[tokens-loading current-viewing-account-address]] + (get tokens-loading current-viewing-account-address))) (rf/reg-sub :wallet/create-account @@ -218,9 +234,9 @@ :or {networks [] size 32}}] (->> accounts - (keep (fn [{:keys [path customization-color emoji name address]}] + (keep (fn [{:keys [path color emoji name address]}] (when-not (string/starts-with? path constants/path-eip1581) - {:account-props {:customization-color customization-color + {:account-props {:customization-color color :size size :emoji emoji :type :default @@ -233,8 +249,8 @@ (defn- format-settings-missing-keypair-accounts [accounts] (->> accounts - (map (fn [{:keys [customization-color emoji]}] - {:customization-color customization-color + (map (fn [{:keys [color emoji]}] + {:customization-color color :emoji emoji :type :default})))) @@ -311,15 +327,15 @@ :wallet/account-cards-data :<- [:wallet/accounts] :<- [:wallet/balances-in-selected-networks] - :<- [:wallet/tokens-loading?] + :<- [:wallet/tokens-loading] :<- [:profile/currency-symbol] - (fn [[accounts balances tokens-loading? currency-symbol]] + (fn [[accounts balances tokens-loading currency-symbol]] (mapv (fn [{:keys [color address watch-only?] :as account}] (assoc account :customization-color color :type (if watch-only? :watch-only :empty) :on-press #(rf/dispatch [:wallet/navigate-to-account address]) - :loading? tokens-loading? + :loading? (get tokens-loading address) :balance (utils/prettify-balance currency-symbol (get balances address)))) accounts))) diff --git a/src/status_im/subs/wallet/wallet_test.cljs b/src/status_im/subs/wallet/wallet_test.cljs index 0a200457dc..c6ba5b66bd 100644 --- a/src/status_im/subs/wallet/wallet_test.cljs +++ b/src/status_im/subs/wallet/wallet_test.cljs @@ -609,46 +609,46 @@ (rf/sub [sub-name]))))) (def chat-account - {:path "m/43'/60'/1581'/0'/0" - :emoji "" - :key-uid "abc" - :address "address-1" - :color-id "" - :wallet false - :name "My Profile" - :type "generated" - :chat true - :customization-color :blue - :hidden false - :removed false}) + {:path "m/43'/60'/1581'/0'/0" + :emoji "" + :key-uid "abc" + :address "address-1" + :color-id "" + :wallet false + :name "My Profile" + :type "generated" + :chat true + :color :blue + :hidden false + :removed false}) (def operable-wallet-account - {:path "m/44'/60'/0'/0/0" - :emoji "🤡" - :key-uid "abc" - :address "address-2" - :wallet true - :name "My Account" - :type "generated" - :chat false - :customization-color :primary - :hidden false - :operable :fully - :removed false}) + {:path "m/44'/60'/0'/0/0" + :emoji "🤡" + :key-uid "abc" + :address "address-2" + :wallet true + :name "My Account" + :type "generated" + :chat false + :color :primary + :hidden false + :operable :fully + :removed false}) (def inoperable-wallet-account - {:path "m/44'/60'/0'/0/0" - :emoji "🧠" - :key-uid "def" - :address "address-3" - :wallet true - :name "My Other Account" - :type "generated" - :chat false - :customization-color :primary - :hidden false - :operable :no - :removed false}) + {:path "m/44'/60'/0'/0/0" + :emoji "🧠" + :key-uid "def" + :address "address-3" + :wallet true + :name "My Other Account" + :type "generated" + :chat false + :color :primary + :hidden false + :operable :no + :removed false}) (def default-keypair-accounts {:key-uid "abc" @@ -686,14 +686,13 @@ {:missing [{:name (:name seed-phrase-keypair-accounts) :key-uid (:key-uid seed-phrase-keypair-accounts) :type (keyword (:type seed-phrase-keypair-accounts)) - :accounts [{:customization-color (:customization-color inoperable-wallet-account) + :accounts [{:customization-color (:color inoperable-wallet-account) :emoji (:emoji inoperable-wallet-account) :type :default}]}] :operable [{:name (:name default-keypair-accounts) :key-uid (:key-uid default-keypair-accounts) :type (keyword (:type default-keypair-accounts)) - :accounts [{:account-props {:customization-color (:customization-color - operable-wallet-account) + :accounts [{:account-props {:customization-color (:color operable-wallet-account) :size 32 :emoji (:emoji operable-wallet-account) :type :default @@ -717,7 +716,7 @@ [:wallet :accounts] {(:address operable-wallet-account) operable-wallet-account})))) - (let [{:keys [customization-color + (let [{:keys [color name address emoji]} operable-wallet-account @@ -730,7 +729,7 @@ :operable [{:name (:name default-keypair-accounts) :key-uid (:key-uid default-keypair-accounts) :type (keyword (:type default-keypair-accounts)) - :accounts [{:account-props {:customization-color customization-color + :accounts [{:account-props {:customization-color color :size size-option :emoji emoji :type :default @@ -763,8 +762,7 @@ :operable [{:name (:name default-keypair-accounts) :key-uid (:key-uid default-keypair-accounts) :type (keyword (:type default-keypair-accounts)) - :accounts [{:account-props {:customization-color (:customization-color - operable-wallet-account) + :accounts [{:account-props {:customization-color (:color operable-wallet-account) :size 32 :emoji (:emoji operable-wallet-account) :type :default diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index 41efc580fb..2a714ebae5 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -68,7 +68,7 @@ (defn wallet-loaded? [] - (not @(rf/subscribe [:wallet/tokens-loading?]))) + (not @(rf/subscribe [:wallet/home-tokens-loading?]))) (defn assert-messenger-started []