diff --git a/resources/images/tokens/mainnet/STT.png b/resources/images/tokens/mainnet/STT.png new file mode 100644 index 0000000000..6a5b152b2e Binary files /dev/null and b/resources/images/tokens/mainnet/STT.png differ diff --git a/resources/images/tokens/mainnet/STT@2x.png b/resources/images/tokens/mainnet/STT@2x.png new file mode 100644 index 0000000000..ffa49bb1dd Binary files /dev/null and b/resources/images/tokens/mainnet/STT@2x.png differ diff --git a/resources/images/tokens/mainnet/STT@3x.png b/resources/images/tokens/mainnet/STT@3x.png new file mode 100644 index 0000000000..7037af0441 Binary files /dev/null and b/resources/images/tokens/mainnet/STT@3x.png differ diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index c3cd62c0f2..a9f8ec200a 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -456,7 +456,9 @@ (def ^:const optimism-network-name :optimism) (def ^:const arbitrum-network-name :arbitrum) -(def ^:const default-network-names #{mainnet-network-name optimism-network-name arbitrum-network-name}) +(def ^:const default-network-names [mainnet-network-name optimism-network-name arbitrum-network-name]) + +(def ^:const default-network-count (count default-network-names)) (def ^:const chain-id-separator ":") diff --git a/src/status_im/contexts/shell/jump_to/components/bottom_tabs/view.cljs b/src/status_im/contexts/shell/jump_to/components/bottom_tabs/view.cljs index 5ae0bffb4e..fb9f2e9b24 100644 --- a/src/status_im/contexts/shell/jump_to/components/bottom_tabs/view.cljs +++ b/src/status_im/contexts/shell/jump_to/components/bottom_tabs/view.cljs @@ -33,7 +33,10 @@ (assoc :test-ID stack-id :icon icon :icon-color-anim icon-color - :on-press #(animation/bottom-tab-on-press stack-id true) + :on-press (fn [] + (when-not (= stack-id :wallet-stack) + (rf/dispatch [:wallet/reset-selected-networks])) + (animation/bottom-tab-on-press stack-id true)) :accessibility-label (str (name stack-id) "-tab") :customization-color customization-color))])) diff --git a/src/status_im/contexts/shell/jump_to/components/home_stack/view.cljs b/src/status_im/contexts/shell/jump_to/components/home_stack/view.cljs index 1f5bd8d882..734a6bc9c5 100644 --- a/src/status_im/contexts/shell/jump_to/components/home_stack/view.cljs +++ b/src/status_im/contexts/shell/jump_to/components/home_stack/view.cljs @@ -9,7 +9,7 @@ [status-im.contexts.shell.jump-to.constants :as shell.constants] [status-im.contexts.shell.jump-to.state :as state] [status-im.contexts.shell.jump-to.utils :as utils] - [status-im.contexts.wallet.home.view :as wallet-new] + [status-im.contexts.wallet.home.view :as wallet] [utils.re-frame :as rf])) (defn load-stack? @@ -32,7 +32,7 @@ (case stack-id :communities-stack [:f> communities/view] :chats-stack [:f> chat/view] - :wallet-stack [wallet-new/view] + :wallet-stack [wallet/view] :browser-stack [browser.stack/browser-stack] [:<>])]) diff --git a/src/status_im/contexts/shell/share/profile/view.cljs b/src/status_im/contexts/shell/share/profile/view.cljs index e6cab6779f..95d86fb7a8 100644 --- a/src/status_im/contexts/shell/share/profile/view.cljs +++ b/src/status_im/contexts/shell/share/profile/view.cljs @@ -37,7 +37,7 @@ :on-text-long-press #(rf/dispatch [:share/copy-text-and-show-toast {:text-to-copy universal-profile-url :post-copy-message (i18n/label :t/link-to-profile-copied)}]) - :profile-picture (:uri (profile.utils/photo profile)) + :profile-picture (profile.utils/photo profile) :full-name (profile.utils/displayed-name profile) :customization-color customization-color}]] diff --git a/src/status_im/contexts/shell/share/wallet/view.cljs b/src/status_im/contexts/shell/share/wallet/view.cljs index 8254852e69..a0aa7a7ee5 100644 --- a/src/status_im/contexts/shell/share/wallet/view.cljs +++ b/src/status_im/contexts/shell/share/wallet/view.cljs @@ -6,6 +6,7 @@ [react-native.platform :as platform] [react-native.share :as share] [reagent.core :as reagent] + [status-im.constants :as constants] [status-im.contexts.shell.share.style :as style] [status-im.contexts.shell.share.wallet.style :as wallet-style] [status-im.contexts.wallet.common.utils :as utils] @@ -33,25 +34,28 @@ :isNewTask true}))) (defn- open-preferences - [selected-networks] - (rf/dispatch [:show-bottom-sheet - {:theme :dark - :shell? true - :content - (fn [] - [network-preferences/view - {:blur? true - :selected-networks (set @selected-networks) - :on-save (fn [chain-ids] - (rf/dispatch [:hide-bottom-sheet]) - (reset! selected-networks (map #(get utils/id->network %) - chain-ids)))}])}])) + [selected-networks account] + (rf/dispatch + [:show-bottom-sheet + {:theme :dark + :shell? true + :content (fn [] + [network-preferences/view + {:blur? true + :selected-networks (set @selected-networks) + :account account + :button-label (i18n/label :t/display) + :on-save (fn [chain-ids] + (rf/dispatch [:hide-bottom-sheet]) + (reset! selected-networks (map #(get utils/id->network %) + chain-ids)))}])}])) + (defn- wallet-qr-code-item [{:keys [account index]}] (let [{window-width :width} (rn/get-window) - selected-networks (reagent/atom [:ethereum :optimism :arbitrum]) + selected-networks (reagent/atom constants/default-network-names) wallet-type (reagent/atom :legacy) - on-settings-press #(open-preferences selected-networks) + on-settings-press #(open-preferences selected-networks account) on-legacy-press #(reset! wallet-type :legacy) on-multichain-press #(reset! wallet-type :multichain)] (fn [] diff --git a/src/status_im/contexts/wallet/account/share_address/view.cljs b/src/status_im/contexts/wallet/account/share_address/view.cljs index eced4be4ed..eddbfeede0 100644 --- a/src/status_im/contexts/wallet/account/share_address/view.cljs +++ b/src/status_im/contexts/wallet/account/share_address/view.cljs @@ -6,6 +6,7 @@ [react-native.safe-area :as safe-area] [react-native.share :as share] [reagent.core :as reagent] + [status-im.constants :as constants] [status-im.contexts.wallet.account.share-address.style :as style] [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences] @@ -34,13 +35,13 @@ [selected-networks] (let [on-save (fn [chain-ids] (rf/dispatch [:hide-bottom-sheet]) - (reset! selected-networks (map #(if (= % 1) :mainnet (utils/id->network %)) - chain-ids))) + (reset! selected-networks (map utils/id->network chain-ids))) sheet-content (fn [] [network-preferences/view {:blur? true :selected-networks (set @selected-networks) - :on-save on-save}])] + :on-save on-save + :button-label (i18n/label :t/display)}])] (rf/dispatch [:show-bottom-sheet {:theme :dark :shell? true @@ -53,7 +54,7 @@ wallet-type (reagent/atom :legacy) ;; Design team is yet to confirm the default selected networks here. Should be the current ;; selected for the account or all the networks always - selected-networks (reagent/atom [:mainnet :optimism :arbitrum]) + selected-networks (reagent/atom constants/default-network-names) on-settings-press #(open-preferences selected-networks) on-legacy-press #(reset! wallet-type :legacy) on-multichain-press #(reset! wallet-type :multichain)] 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 69977ebc61..96cbabfbb0 100644 --- a/src/status_im/contexts/wallet/account/tabs/assets/view.cljs +++ b/src/status_im/contexts/wallet/account/tabs/assets/view.cljs @@ -7,8 +7,9 @@ (defn view [] - (let [tokens-loading? (rf/sub [:wallet/tokens-loading?]) - tokens (rf/sub [:wallet/account-token-values])] + (let [tokens-loading? (rf/sub [:wallet/tokens-loading?]) + tokens (rf/sub [:wallet/current-viewing-account-token-values]) + {:keys [watch-only?]} (rf/sub [:wallet/current-viewing-account])] (if tokens-loading? [quo/skeleton-list {:content :assets @@ -18,4 +19,5 @@ {:render-fn token-value/view :style {:flex 1} :data tokens + :render-data {:watch-only? watch-only?} :content-container-style {:padding-horizontal 8}}]))) diff --git a/src/status_im/contexts/wallet/account/tabs/view.cljs b/src/status_im/contexts/wallet/account/tabs/view.cljs index 6c01bca1ff..5b1f968511 100644 --- a/src/status_im/contexts/wallet/account/tabs/view.cljs +++ b/src/status_im/contexts/wallet/account/tabs/view.cljs @@ -12,7 +12,7 @@ (defn view [{:keys [selected-tab]}] - (let [collectible-list (rf/sub [:wallet/current-viewing-account-collectibles])] + (let [collectible-list (rf/sub [:wallet/current-viewing-account-collectibles-in-selected-networks])] [rn/view {:style {:flex 1}} (case selected-tab :assets [assets/view] diff --git a/src/status_im/contexts/wallet/bridge/bridge_to/view.cljs b/src/status_im/contexts/wallet/bridge/bridge_to/view.cljs index 30180a89a0..f318d28ace 100644 --- a/src/status_im/contexts/wallet/bridge/bridge_to/view.cljs +++ b/src/status_im/contexts/wallet/bridge/bridge_to/view.cljs @@ -14,20 +14,20 @@ (defn- bridge-token-component [] (fn [{:keys [chain-id network-name]} token] - (let [network (rf/sub [:wallet/network-details-by-chain-id chain-id]) - currency (rf/sub [:profile/currency]) - currency-symbol (rf/sub [:profile/currency-symbol]) - all-balances (:balances-per-chain token) - balance-for-chain (utils/get-balance-for-chain all-balances chain-id) - crypto-formatted (or (:balance balance-for-chain) "0.00") - fiat-value (utils/token-fiat-value currency - (or (:balance balance-for-chain) 0) - token) - fiat-formatted (utils/get-standard-fiat-format crypto-formatted currency-symbol fiat-value)] + (let [network (rf/sub [:wallet/network-details-by-chain-id chain-id]) + currency (rf/sub [:profile/currency]) + currency-symbol (rf/sub [:profile/currency-symbol]) + balance (utils/calculate-total-token-balance token [chain-id]) + crypto-value (utils/get-standard-crypto-format token balance) + fiat-value (utils/calculate-token-fiat-value + {:currency currency + :balance balance + :token token}) + fiat-formatted (utils/get-standard-fiat-format crypto-value currency-symbol fiat-value)] [quo/network-list {:label (name network-name) :network-image (quo.resources/get-network (:network-name network)) - :token-value (str crypto-formatted " " (:symbol token)) + :token-value (str crypto-value " " (:symbol token)) :fiat-value fiat-formatted :on-press #(rf/dispatch [:wallet/select-bridge-network {:network-chain-id chain-id diff --git a/src/status_im/contexts/wallet/common/account_switcher/view.cljs b/src/status_im/contexts/wallet/common/account_switcher/view.cljs index bae23f9255..e7c45022cc 100644 --- a/src/status_im/contexts/wallet/common/account_switcher/view.cljs +++ b/src/status_im/contexts/wallet/common/account_switcher/view.cljs @@ -1,10 +1,10 @@ (ns status-im.contexts.wallet.common.account-switcher.view - (:require [quo.core :as quo] - [status-im.contexts.wallet.sheets.account-options.view :as account-options] - [status-im.contexts.wallet.sheets.network-filter.view :as network-filter] - [status-im.contexts.wallet.sheets.select-account.view :as select-account] - [status-im.feature-flags :as ff] - [utils.re-frame :as rf])) + (:require + [quo.core :as quo] + [status-im.contexts.wallet.sheets.account-options.view :as account-options] + [status-im.contexts.wallet.sheets.network-filter.view :as network-filter] + [status-im.contexts.wallet.sheets.select-account.view :as select-account] + [utils.re-frame :as rf])) (defn get-bottom-sheet-args [switcher-type] @@ -20,7 +20,7 @@ accessibility-label :top-bar switcher-type :account-options}}] (let [{:keys [color emoji watch-only?]} (rf/sub [:wallet/current-viewing-account]) - networks (rf/sub [:wallet/network-details])] + networks (rf/sub [:wallet/selected-network-details])] [quo/page-nav {:type (or type :no-title) :icon-name icon-name @@ -29,10 +29,7 @@ :on-press on-press :accessibility-label accessibility-label :networks networks - :networks-on-press #(ff/alert ::ff/wallet.network-filter - (fn [] - (rf/dispatch [:show-bottom-sheet - {:content network-filter/view}]))) + :networks-on-press #(rf/dispatch [:show-bottom-sheet {:content network-filter/view}]) :right-side :account-switcher :account-switcher {:customization-color color :on-press #(rf/dispatch [:show-bottom-sheet diff --git a/src/status_im/contexts/wallet/common/asset_list/view.cljs b/src/status_im/contexts/wallet/common/asset_list/view.cljs index 76829fc20f..0209a841b5 100644 --- a/src/status_im/contexts/wallet/common/asset_list/view.cljs +++ b/src/status_im/contexts/wallet/common/asset_list/view.cljs @@ -6,10 +6,12 @@ [utils.re-frame :as rf])) (defn- asset-component - [token _ _ {:keys [currency currency-symbol on-token-press]}] - (let [token-units (utils/total-token-units-in-all-chains token) - crypto-formatted (utils/get-standard-crypto-format token token-units) - fiat-value (utils/total-token-fiat-value currency token) + [{:keys [total-balance] :as token} _ _ {:keys [currency currency-symbol on-token-press]}] + (let [fiat-value (utils/calculate-token-fiat-value + {:currency currency + :balance total-balance + :token token}) + crypto-formatted (utils/get-standard-crypto-format token total-balance) fiat-formatted (utils/get-standard-fiat-format crypto-formatted currency-symbol fiat-value)] [quo/token-network {:token (:symbol token) @@ -21,7 +23,7 @@ (defn view [{:keys [search-text on-token-press]}] - (let [filtered-tokens (rf/sub [:wallet/tokens-filtered search-text]) + (let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered search-text]) currency (rf/sub [:profile/currency]) currency-symbol (rf/sub [:profile/currency-symbol])] [rn/flat-list diff --git a/src/status_im/contexts/wallet/common/temp.cljs b/src/status_im/contexts/wallet/common/temp.cljs index e661935a30..2ed1df5fa9 100644 --- a/src/status_im/contexts/wallet/common/temp.cljs +++ b/src/status_im/contexts/wallet/common/temp.cljs @@ -43,37 +43,3 @@ :image :icon-avatar :image-props {:icon (status.resources/get-service-image :latamex)} :on-press #(rn/open-url "https://latamex.com")}]) - -(defn bridge-token-list - [networks-list] - [{:token :snt - :name "Status" - :token-value "0.00 SNT" - :fiat-value "€0.00" - :networks networks-list - :state :default - :symbol "STT" - :customization-color :blue} - {:token :eth - :name "Ethereum" - :token-value "0.00 ETH" - :fiat-value "€0.00" - :networks networks-list - :state :default - :symbol "ETH" - :customization-color :blue} - {:token :dai - :name "Dai" - :token-value "0.00 DAI" - :fiat-value "€0.00" - :networks networks-list - :state :default - :symbol "DAI" - :customization-color :blue}]) - -(def secret-phrase - ["witch" "collapse" "practice" "feed" "shame" "open" "lion" - "collapse" "umbrella" "fabric" "sadness" "obligue"]) -(def random-words - ["cousin" "roof" "minute" "swallow" "wing" "motion" "stomach" - "abuse" "banner" "noble" "poet" "wrist"]) diff --git a/src/status_im/contexts/wallet/common/token_value/view.cljs b/src/status_im/contexts/wallet/common/token_value/view.cljs index 08b53e6ff6..c0a194f496 100644 --- a/src/status_im/contexts/wallet/common/token_value/view.cljs +++ b/src/status_im/contexts/wallet/common/token_value/view.cljs @@ -5,7 +5,7 @@ (defn token-value-drawer [token] - (let [token-data (first (rf/sub [:wallet/tokens-filtered (:token token)]))] + (let [token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered (:token token)]))] [:<> [quo/action-drawer [[{:icon :i/buy @@ -41,13 +41,12 @@ :on-press #(js/alert "to be implemented")}]]]])) (defn view - [item] - (let [{:keys [watch-only?]} (rf/sub [:wallet/current-viewing-account])] - [quo/token-value - (cond-> item - (not watch-only?) - (assoc :on-long-press - #(rf/dispatch - [:show-bottom-sheet - {:content (fn [] [token-value-drawer item]) - :selected-item (fn [] [quo/token-value item])}])))])) + [item _ _ {:keys [watch-only?]}] + [quo/token-value + (cond-> item + (not watch-only?) + (assoc :on-long-press + #(rf/dispatch + [:show-bottom-sheet + {:content (fn [] [token-value-drawer item]) + :selected-item (fn [] [quo/token-value item])}])))]) diff --git a/src/status_im/contexts/wallet/common/utils.cljs b/src/status_im/contexts/wallet/common/utils.cljs index 76813b79a0..30e0187a32 100644 --- a/src/status_im/contexts/wallet/common/utils.cljs +++ b/src/status_im/contexts/wallet/common/utils.cljs @@ -86,61 +86,48 @@ (str "<" (remove-trailing-zeroes (.toFixed one-cent-value decimals-count))) (remove-trailing-zeroes (.toFixed token-units decimals-count)))))) -(defn total-token-units-in-all-chains - [{:keys [balances-per-chain decimals] :as _token}] - (-> balances-per-chain - (total-raw-balance-in-all-chains) - (money/token->unit decimals))) +(defn get-market-value + [currency {:keys [market-values-per-currency]}] + (or (get-in market-values-per-currency + [currency :price]) + (get-in market-values-per-currency + [constants/profile-default-currency :price]) + ;; NOTE: adding fallback value (zero) in case prices are + ;; unavailable and to prevent crash on calculating fiat value + 0)) + +(defn- filter-chains + [balances-per-chain chain-ids] + (if chain-ids + (select-keys balances-per-chain chain-ids) + balances-per-chain)) + +(defn calculate-total-token-balance + ([token] + (calculate-total-token-balance token nil)) + ([{:keys [balances-per-chain decimals]} chain-ids] + (-> balances-per-chain + (filter-chains chain-ids) + (total-raw-balance-in-all-chains) + (money/token->unit decimals)))) (defn get-account-by-address [accounts address] (some #(when (= (:address %) address) %) accounts)) -(defn total-token-fiat-value - "Returns the total token fiat value taking into account all token's chains." - [currency {:keys [market-values-per-currency] :as token}] - (let [price (or (get-in market-values-per-currency - [currency :price]) - (get-in market-values-per-currency - [constants/profile-default-currency :price]) - ;; NOTE: adding fallback value (zero) in case prices are - ;; unavailable and to prevent crash on calculating fiat value - 0) - total-units-in-all-chains (total-token-units-in-all-chains token)] - (money/crypto->fiat total-units-in-all-chains price))) - -(defn token-fiat-value - "Returns the fiat value for a single token on a given network." - [currency raw-balance {:keys [market-values-per-currency]}] - (let [price (or (get-in market-values-per-currency - [currency :price]) - (get-in market-values-per-currency - [constants/profile-default-currency :price]) - 0)] - (money/crypto->fiat raw-balance price))) - -(defn calculate-balance-for-account - [currency {:keys [tokens] :as _account}] - (->> tokens - (map #(total-token-fiat-value currency %)) - (reduce money/add))) - -(defn calculate-balance-for-token - [token] - (money/bignumber - (money/mul (total-token-units-in-all-chains token) - (-> token :market-values-per-currency :usd :price)))) - -(defn calculate-balance - [tokens-in-account] - (->> tokens-in-account - (map #(calculate-balance-for-token %)) - (reduce +))) +(defn calculate-token-fiat-value + "Returns the token fiat value for provided raw balance" + [{:keys [currency balance token]}] + (let [price (get-market-value currency token)] + (money/crypto->fiat balance price))) (defn calculate-balance-from-tokens - [{:keys [currency tokens]}] + [{:keys [currency tokens chain-ids]}] (->> tokens - (map #(total-token-fiat-value currency %)) + (map #(calculate-token-fiat-value + {:currency currency + :balance (calculate-total-token-balance % chain-ids) + :token %})) (reduce money/add))) (defn- add-balances-per-chain @@ -191,38 +178,54 @@ address)) (def id->network - {constants/ethereum-mainnet-chain-id :ethereum - constants/ethereum-goerli-chain-id :ethereum - constants/ethereum-sepolia-chain-id :ethereum - constants/optimism-mainnet-chain-id :optimism - constants/optimism-goerli-chain-id :optimism - constants/optimism-sepolia-chain-id :optimism - constants/arbitrum-mainnet-chain-id :arbitrum - constants/arbitrum-goerli-chain-id :arbitrum - constants/arbitrum-sepolia-chain-id :arbitrum}) + {constants/ethereum-mainnet-chain-id constants/mainnet-network-name + constants/ethereum-goerli-chain-id constants/mainnet-network-name + constants/ethereum-sepolia-chain-id constants/mainnet-network-name + constants/optimism-mainnet-chain-id constants/optimism-network-name + constants/optimism-goerli-chain-id constants/optimism-network-name + constants/optimism-sepolia-chain-id constants/optimism-network-name + constants/arbitrum-mainnet-chain-id constants/arbitrum-network-name + constants/arbitrum-goerli-chain-id constants/arbitrum-network-name + constants/arbitrum-sepolia-chain-id constants/arbitrum-network-name}) (defn- get-chain-id - [testnet-enabled? goerli-enabled?] + [{:keys [mainnet-chain-id sepolia-chain-id goerli-chain-id testnet-enabled? goerli-enabled?]}] (cond (and testnet-enabled? goerli-enabled?) - {:eth constants/ethereum-goerli-chain-id - :opt constants/optimism-goerli-chain-id - :arb1 constants/arbitrum-goerli-chain-id} + goerli-chain-id testnet-enabled? - {:eth constants/ethereum-sepolia-chain-id - :opt constants/optimism-sepolia-chain-id - :arb1 constants/arbitrum-sepolia-chain-id} + sepolia-chain-id :else - {:eth constants/ethereum-mainnet-chain-id - :opt constants/optimism-mainnet-chain-id - :arb1 constants/arbitrum-mainnet-chain-id})) + mainnet-chain-id)) -(defn short-name->id - [short-name testnet-enabled? goerli-enabled?] - (let [chain-id-map (get-chain-id testnet-enabled? goerli-enabled?)] - (get chain-id-map short-name))) +(defn network->chain-id + [{:keys [network testnet-enabled? goerli-enabled?]}] + (condp contains? (keyword network) + #{constants/mainnet-network-name (keyword constants/mainnet-short-name)} + (get-chain-id + {:mainnet-chain-id constants/ethereum-mainnet-chain-id + :sepolia-chain-id constants/ethereum-sepolia-chain-id + :goerli-chain-id constants/ethereum-goerli-chain-id + :testnet-enabled? testnet-enabled? + :goerli-enabled? goerli-enabled?}) + + #{constants/optimism-network-name (keyword constants/optimism-short-name)} + (get-chain-id + {:mainnet-chain-id constants/optimism-mainnet-chain-id + :sepolia-chain-id constants/optimism-sepolia-chain-id + :goerli-chain-id constants/optimism-goerli-chain-id + :testnet-enabled? testnet-enabled? + :goerli-enabled? goerli-enabled?}) + + #{constants/arbitrum-network-name (keyword constants/arbitrum-short-name)} + (get-chain-id + {:mainnet-chain-id constants/arbitrum-mainnet-chain-id + :sepolia-chain-id constants/arbitrum-sepolia-chain-id + :goerli-chain-id constants/arbitrum-goerli-chain-id + :testnet-enabled? testnet-enabled? + :goerli-enabled? goerli-enabled?}))) (defn get-standard-fiat-format [crypto-value currency-symbol fiat-value] @@ -241,8 +244,11 @@ (defn calculate-token-value "This function returns token values in the props of token-value (quo) component" [{:keys [token color currency currency-symbol]}] - (let [token-units (total-token-units-in-all-chains token) - fiat-value (total-token-fiat-value currency token) + (let [balance (calculate-total-token-balance token) + fiat-value (calculate-token-fiat-value + {:currency currency + :balance balance + :token token}) market-values (or (get-in token [:market-values-per-currency currency]) (get-in token [:market-values-per-currency @@ -250,7 +256,7 @@ {:keys [price change-pct-24hour]} market-values formatted-token-price (prettify-balance currency-symbol price) percentage-change (prettify-percentage-change change-pct-24hour) - crypto-value (get-standard-crypto-format token token-units) + crypto-value (get-standard-crypto-format token balance) fiat-value (get-standard-fiat-format crypto-value currency-symbol fiat-value)] @@ -280,15 +286,10 @@ (let [split-result (string/split input-string #"0x")] [(first split-result) (str "0x" (second split-result))])) -(defn get-balance-for-chain - [data chain-id] - (some #(when (= chain-id (:chain-id %)) %) (vals data))) - (defn make-network-item "This function generates props for quo/category component item" - [{:keys [network-name] :as _network} - {:keys [title color on-change networks state label-props] :as _options}] - (cond-> {:title (or title (string/capitalize (name network-name))) + [{:keys [network-name color on-change networks state label-props]}] + (cond-> {:title (string/capitalize (name network-name)) :image :icon-avatar :image-props {:icon (resources/get-network network-name) :size :size-20} @@ -315,3 +316,21 @@ :else constants/mainnet-chain-ids)) + +(defn filter-tokens-in-chains + [tokens chain-ids] + (map #(update % :balances-per-chain select-keys chain-ids) tokens)) + +(defn calculate-balances-per-chain + [{:keys [tokens currency currency-symbol]}] + (-> + (reduce (fn [acc {:keys [balances-per-chain decimals] :as token}] + (let [currency-value (get-market-value currency token) + fiat-balance-per-chain (update-vals balances-per-chain + #(-> (money/token->unit (:raw-balance %) + decimals) + (money/crypto->fiat currency-value)))] + (merge-with money/add acc fiat-balance-per-chain))) + {} + tokens) + (update-vals #(prettify-balance currency-symbol %)))) diff --git a/src/status_im/contexts/wallet/common/utils_test.cljs b/src/status_im/contexts/wallet/common/utils_test.cljs index d7b3485bb5..2aa8019db6 100644 --- a/src/status_im/contexts/wallet/common/utils_test.cljs +++ b/src/status_im/contexts/wallet/common/utils_test.cljs @@ -1,7 +1,9 @@ (ns status-im.contexts.wallet.common.utils-test - (:require [cljs.test :refer [deftest is testing]] - [status-im.contexts.wallet.common.utils :as utils] - [utils.money :as money])) + (:require + [cljs.test :refer [deftest is testing]] + [status-im.constants :as constants] + [status-im.contexts.wallet.common.utils :as utils] + [utils.money :as money])) (deftest test-get-first-name (testing "get-first-name function" @@ -28,7 +30,6 @@ (is (= (utils/format-derivation-path "m/44'/60'/0'/0/0") "m / 44' / 60' / 0' / 0 / 0")) (is (= (utils/format-derivation-path "m/44'/60'/0'/0/123") "m / 44' / 60' / 0' / 0 / 123")))) - (deftest test-get-formatted-derivation-path (testing "get-formatted-derivation-path function" (is (= (utils/get-formatted-derivation-path 5) "m / 44' / 60' / 0' / 0 / 5")) @@ -70,13 +71,13 @@ token-units) "<2"))))) -(deftest test-total-token-units-in-all-chains - (testing "total-token-units-in-all-chains function" +(deftest test-calculate-total-token-balance + (testing "calculate-total-token-balance function" (let [token {:balances-per-chain {1 {:raw-balance (money/bignumber 100)} 10 {:raw-balance (money/bignumber 200)} 42161 {:raw-balance (money/bignumber 300)}} :decimals 2}] - (is (money/equal-to (utils/total-token-units-in-all-chains token) 6.0))))) + (is (money/equal-to (utils/calculate-total-token-balance token) 6.0))))) (deftest test-get-account-by-address (testing "get-account-by-address function" @@ -115,3 +116,20 @@ (is (= (utils/prettify-percentage-change 1.113454) "1.11")) (is (= (utils/prettify-percentage-change -0.35) "0.35")) (is (= (utils/prettify-percentage-change -0.78234) "0.78")))) + +(deftest test-network->chain-id + (testing "network->chain-id function" + (is (= (utils/network->chain-id {:network :mainnet :testnet-enabled? false :goerli-enabled? false}) + constants/ethereum-mainnet-chain-id)) + (is (= (utils/network->chain-id {:network :eth :testnet-enabled? true :goerli-enabled? false}) + constants/ethereum-sepolia-chain-id)) + (is (= (utils/network->chain-id {:network "optimism" :testnet-enabled? true :goerli-enabled? false}) + constants/optimism-sepolia-chain-id)) + (is (= (utils/network->chain-id {:network "opt" :testnet-enabled? false :goerli-enabled? true}) + constants/optimism-mainnet-chain-id)) + (is (= (utils/network->chain-id {:network :opt :testnet-enabled? true :goerli-enabled? true}) + constants/optimism-goerli-chain-id)) + (is (= (utils/network->chain-id {:network :arb1 :testnet-enabled? false :goerli-enabled? false}) + constants/arbitrum-mainnet-chain-id)) + (is (= (utils/network->chain-id {:network :arbitrum :testnet-enabled? true :goerli-enabled? false}) + constants/arbitrum-sepolia-chain-id)))) diff --git a/src/status_im/contexts/wallet/db.cljs b/src/status_im/contexts/wallet/db.cljs new file mode 100644 index 0000000000..6d98804f13 --- /dev/null +++ b/src/status_im/contexts/wallet/db.cljs @@ -0,0 +1,9 @@ +(ns status-im.contexts.wallet.db + (:require [status-im.constants :as constants])) + +(def network-filter-defaults + {:selector-state :default + :selected-networks (set constants/default-network-names)}) + +(def defaults + {: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 5d38c409e8..10b3ff35f1 100644 --- a/src/status_im/contexts/wallet/events.cljs +++ b/src/status_im/contexts/wallet/events.cljs @@ -3,9 +3,11 @@ [clojure.string :as string] [react-native.background-timer :as background-timer] [react-native.platform :as platform] + [status-im.constants :as constants] [status-im.contexts.wallet.accounts.add-account.address-to-watch.events] [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.data-store :as data-store] + [status-im.contexts.wallet.db :as db] [status-im.contexts.wallet.events.collectibles] [status-im.contexts.wallet.item-types :as item-types] [taoensso.timbre :as log] @@ -443,3 +445,33 @@ :type :negative :text (i18n/label :t/provider-is-down {:chains chain-names}) :duration 10000}]]])}))) + +(defn reset-selected-networks + [{:keys [db]}] + {:db (assoc-in db [:wallet :ui :network-filter] db/network-filter-defaults)}) + +(rf/reg-event-fx :wallet/reset-selected-networks reset-selected-networks) + +(defn update-selected-networks + [{:keys [db]} [network-name]] + (let [selected-networks (get-in db [:wallet :ui :network-filter :selected-networks]) + selector-state (get-in db [:wallet :ui :network-filter :selector-state]) + contains-network? (contains? selected-networks network-name) + update-fn (if contains-network? disj conj) + networks-count (count selected-networks)] + (cond (= selector-state :default) + {:db (-> db + (assoc-in [:wallet :ui :network-filter :selected-networks] #{network-name}) + (assoc-in [:wallet :ui :network-filter :selector-state] :changed))} + + ;; reset the list + ;; - if user is removing the last network in the list + ;; - if all networks is selected + (or (and (= networks-count 1) contains-network?) + (and (= (inc networks-count) constants/default-network-count) (not contains-network?))) + {:fx [[:dispatch [:wallet/reset-selected-networks]]]} + + :else + {:db (update-in db [:wallet :ui :network-filter :selected-networks] update-fn network-name)}))) + +(rf/reg-event-fx :wallet/update-selected-networks update-selected-networks) diff --git a/src/status_im/contexts/wallet/events_test.cljs b/src/status_im/contexts/wallet/events_test.cljs index 45e2960f7d..7568e4438b 100644 --- a/src/status_im/contexts/wallet/events_test.cljs +++ b/src/status_im/contexts/wallet/events_test.cljs @@ -2,6 +2,8 @@ (:require [cljs.test :refer-macros [deftest is testing]] matcher-combinators.test + [status-im.constants :as constants] + [status-im.contexts.wallet.db :as db] [status-im.contexts.wallet.events :as events] [status-im.contexts.wallet.events.collectibles :as collectibles])) @@ -73,3 +75,46 @@ effects (collectibles/store-last-collectible-details {:db db} [last-collectible]) result-db (:db effects)] (is (match? result-db expected-db))))) + +(deftest reset-selected-networks + (testing "reset-selected-networks" + (let [db {:wallet {}} + expected-db {:wallet db/defaults} + effects (events/reset-selected-networks {:db db}) + result-db (:db effects)] + (is (match? result-db expected-db))))) + +(deftest update-selected-networks + (testing "update-selected-networks" + (let [db {:wallet {:ui {:network-filter {:selected-networks + #{constants/optimism-network-name} + :selector-state :changed}}}} + network-name constants/arbitrum-network-name + expected-db {:wallet {:ui {:network-filter {:selected-networks + #{constants/optimism-network-name + network-name} + :selector-state :changed}}}} + props [network-name] + effects (events/update-selected-networks {:db db} props) + result-db (:db effects)] + (is (match? result-db expected-db)))) + + (testing "update-selected-networks > if all networks is already selected, update to incoming network" + (let [db {:wallet db/defaults} + network-name constants/arbitrum-network-name + expected-db {:wallet {:ui {:network-filter {:selected-networks #{network-name} + :selector-state :changed}}}} + props [network-name] + effects (events/update-selected-networks {:db db} props) + result-db (:db effects)] + (is (match? result-db expected-db)))) + + (testing "update-selected-networks > reset on removing last network" + (let [db {:wallet {:ui {:network-filter {:selected-networks + #{constants/optimism-network-name} + :selector-state :changed}}}} + expected-fx [[:dispatch [:wallet/reset-selected-networks]]] + props [constants/optimism-network-name] + effects (events/update-selected-networks {:db db} props) + result-fx (:fx effects)] + (is (match? result-fx expected-fx))))) 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 2c04c72ea6..83eed69b15 100644 --- a/src/status_im/contexts/wallet/home/tabs/assets/view.cljs +++ b/src/status_im/contexts/wallet/home/tabs/assets/view.cljs @@ -12,7 +12,7 @@ (defn view [] (let [tokens-loading? (rf/sub [:wallet/tokens-loading?]) - {:keys [tokens]} (rf/sub [:wallet/aggregated-tokens-and-balance])] + {:keys [tokens]} (rf/sub [:wallet/aggregated-token-values-and-balance])] (if tokens-loading? [quo/skeleton-list {:content :assets diff --git a/src/status_im/contexts/wallet/home/tabs/view.cljs b/src/status_im/contexts/wallet/home/tabs/view.cljs index 845d0b653d..60e88daeb5 100644 --- a/src/status_im/contexts/wallet/home/tabs/view.cljs +++ b/src/status_im/contexts/wallet/home/tabs/view.cljs @@ -9,7 +9,7 @@ (defn view [{:keys [selected-tab]}] - (let [collectible-list (rf/sub [:wallet/all-collectibles-list]) + (let [collectible-list (rf/sub [:wallet/all-collectibles-list-in-selected-networks]) request-collectibles #(rf/dispatch [:wallet/request-collectibles-for-all-accounts {}])] [rn/view {:style style/container} diff --git a/src/status_im/contexts/wallet/home/view.cljs b/src/status_im/contexts/wallet/home/view.cljs index 578a7ea188..307de25720 100644 --- a/src/status_im/contexts/wallet/home/view.cljs +++ b/src/status_im/contexts/wallet/home/view.cljs @@ -6,7 +6,6 @@ [status-im.contexts.wallet.home.style :as style] [status-im.contexts.wallet.home.tabs.view :as tabs] [status-im.contexts.wallet.sheets.network-filter.view :as network-filter] - [status-im.feature-flags :as ff] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -41,18 +40,16 @@ (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?]) - networks (rf/sub [:wallet/network-details]) + 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)) - - {:keys [formatted-balance]} (rf/sub [:wallet/aggregated-tokens-and-balance])] + {:keys [formatted-balance]} (rf/sub [:wallet/aggregated-token-values-and-balance])] (rn/use-effect (fn [] (when (and @account-list-ref (pos? (count cards))) (.scrollToOffset ^js @account-list-ref #js {:animated true - :offset 0} - ))) + :offset 0}))) [(count cards)]) [rn/view {:style (style/home-container)} [common.top-nav/view] @@ -63,10 +60,7 @@ :metrics :none :balance formatted-balance :networks networks - :dropdown-on-press #(ff/alert ::ff/wallet.network-filter - (fn [] - (rf/dispatch [:show-bottom-sheet - {:content network-filter/view}])))}]] + :dropdown-on-press #(rf/dispatch [:show-bottom-sheet {:content network-filter/view}])}]] [quo/wallet-graph {:time-frame :empty}] [rn/flat-list {:ref #(reset! account-list-ref %) diff --git a/src/status_im/contexts/wallet/send/events.cljs b/src/status_im/contexts/wallet/send/events.cljs index 16e4bb21f1..e9f3a4782f 100644 --- a/src/status_im/contexts/wallet/send/events.cljs +++ b/src/status_im/contexts/wallet/send/events.cljs @@ -53,12 +53,16 @@ :wallet/select-send-address (fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]] (let [[prefix to-address] (utils/split-prefix-and-address address) - test-net? (get-in db [:profile/profile :test-networks-enabled?]) + testnet-enabled? (get-in db [:profile/profile :test-networks-enabled?]) goerli-enabled? (get-in db [:profile/profile :is-goerli-enabled?]) prefix-seq (string/split prefix #":") selected-networks (->> prefix-seq (remove string/blank?) - (mapv #(utils/short-name->id (keyword %) test-net? goerli-enabled?)))] + (mapv + #(utils/network->chain-id + {:network % + :testnet-enabled? testnet-enabled? + :goerli-enabled? goerli-enabled?})))] {:db (-> db (assoc-in [:wallet :ui :send :recipient] (or recipient address)) (assoc-in [:wallet :ui :send :to-address] to-address) diff --git a/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs b/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs index 198696ce4f..2fa8e65707 100644 --- a/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs +++ b/src/status_im/contexts/wallet/send/input_amount/component_spec.cljs @@ -12,9 +12,9 @@ (def sub-mocks {:profile/profile {:currency :usd} - :wallet/network-details [{:source 525 + :wallet/selected-network-details [{:source 525 :short-name "eth" - :network-name :ethereum + :network-name :mainnet :chain-id 1 :related-chain-id 5}] :wallet/current-viewing-account {:path "m/44'/60'/0'/0/1" @@ -30,7 +30,7 @@ :color :purple :hidden false :prod-preferred-chain-ids #{1 10 42161} - :network-preferences-names #{:ethereum :arbitrum + :network-preferences-names #{:mainnet :arbitrum :optimism} :position 1 :clock 1698945829328 diff --git a/src/status_im/contexts/wallet/send/input_amount/view.cljs b/src/status_im/contexts/wallet/send/input_amount/view.cljs index 0337985485..6e274ce7b7 100644 --- a/src/status_im/contexts/wallet/send/input_amount/view.cljs +++ b/src/status_im/contexts/wallet/send/input_amount/view.cljs @@ -236,9 +236,10 @@ (utils/get-standard-crypto-format native-token fee-in-native-token)) fee-in-fiat (when-not confirm-disabled? - (utils/token-fiat-value fiat-currency - fee-in-native-token - native-token)) + (utils/calculate-token-fiat-value + {:currency fiat-currency + :balance fee-in-native-token + :token native-token})) currency-symbol (rf/sub [:profile/currency-symbol]) fee-formatted (when fee-in-fiat (utils/get-standard-fiat-format fee-in-crypto-formatted diff --git a/src/status_im/contexts/wallet/send/select_address/tabs/view.cljs b/src/status_im/contexts/wallet/send/select_address/tabs/view.cljs index 461e613540..fcca70f35e 100644 --- a/src/status_im/contexts/wallet/send/select_address/tabs/view.cljs +++ b/src/status_im/contexts/wallet/send/select_address/tabs/view.cljs @@ -2,21 +2,12 @@ (:require [quo.core :as quo] [quo.theme :as quo.theme] - [react-native.gesture :as gesture] + [react-native.core :as rn] [status-im.common.resources :as resources] [status-im.contexts.wallet.send.select-address.tabs.style :as style] [utils.i18n :as i18n] [utils.re-frame :as rf])) -(defn- render-account-item - [{:keys [color address] :as account}] - [quo/account-item - {:account-props (assoc account :customization-color color) - :on-press #(rf/dispatch [:wallet/select-send-address - {:address address - :recipient account - :stack-id :screen/wallet.select-address}])}]) - (defn my-accounts [theme] (let [other-accounts (rf/sub [:wallet/accounts-without-current-viewing-account])] @@ -26,11 +17,15 @@ :description (i18n/label :t/here-is-a-cat-in-a-box-instead) :image (resources/get-themed-image :cat-in-box theme) :container-style style/empty-container-style}] - [gesture/flat-list - {:data other-accounts - :render-fn render-account-item - :content-container-style style/my-accounts-container - :shows-vertical-scroll-indicator false}]))) + (into [rn/view {:style style/my-accounts-container}] + (map (fn [{:keys [color address] :as account}] + [quo/account-item + {:account-props (assoc account :customization-color color) + :on-press #(rf/dispatch [:wallet/select-send-address + {:address address + :recipient account + :stack-id :screen/wallet.select-address}])}])) + other-accounts)))) (defn view-internal [{:keys [selected-tab theme]}] diff --git a/src/status_im/contexts/wallet/sheets/network_filter/view.cljs b/src/status_im/contexts/wallet/sheets/network_filter/view.cljs index 0084341784..e14aa23446 100644 --- a/src/status_im/contexts/wallet/sheets/network_filter/view.cljs +++ b/src/status_im/contexts/wallet/sheets/network_filter/view.cljs @@ -1,54 +1,48 @@ (ns status-im.contexts.wallet.sheets.network-filter.view (:require [quo.core :as quo] - [reagent.core :as reagent] - [status-im.constants :as constants] [status-im.contexts.wallet.common.utils :as utils] [utils.i18n :as i18n] [utils.re-frame :as rf])) (defn view [] - (let [state (reagent/atom :default) - networks-selected (reagent/atom #{}) - toggle-network (fn [network-name] - (reset! state :changed) - (if (contains? @networks-selected - network-name) - (swap! networks-selected disj - network-name) - (swap! networks-selected conj - network-name))) - get-networks (fn [] - (if (= @state :default) - constants/default-network-names - @networks-selected))] - (fn [] - (let [color (rf/sub [:profile/customization-color]) - network-details (rf/sub [:wallet/network-details]) - mainnet (first network-details) - layer-2-networks (rest network-details)] - [:<> - [quo/drawer-top {:title (i18n/label :t/select-networks)}] - [quo/category - {:list-type :settings - :data [(utils/make-network-item mainnet - {:state @state - :title (i18n/label :t/mainnet) - :color color - :networks (get-networks) - :on-change #(toggle-network (:network-name - mainnet)) - :label-props "$0.00"})]}] - [quo/category - {:list-type :settings - :label (i18n/label :t/layer-2) - :data (mapv (fn [network] - (utils/make-network-item network - {:state @state - :color color - :networks (get-networks) - :on-change #(toggle-network (:network-name - network)) - :label-props "$0.00"})) - layer-2-networks)}]])))) + (let [selected-networks (rf/sub [:wallet/selected-networks]) + selector-state (rf/sub [:wallet/network-filter-selector-state]) + color (rf/sub [:profile/customization-color]) + network-details (rf/sub [:wallet/network-details]) + viewing-account? (rf/sub [:wallet/viewing-account?]) + balance-per-chain (if viewing-account? + (rf/sub [:wallet/current-viewing-account-fiat-balance-per-chain]) + (rf/sub [:wallet/aggregated-fiat-balance-per-chain])) + mainnet (first network-details) + layer-2-networks (rest network-details)] + [:<> + [quo/drawer-top {:title (i18n/label :t/select-networks)}] + [quo/category + {:list-type :settings + :data [(utils/make-network-item + {:state selector-state + :network-name (:network-name mainnet) + :color color + :networks selected-networks + :label-props (get balance-per-chain (:chain-id mainnet)) + :on-change #(rf/dispatch + [:wallet/update-selected-networks + (:network-name + mainnet)])})]}] + [quo/category + {:list-type :settings + :label (i18n/label :t/layer-2) + :data (mapv (fn [network] + (utils/make-network-item + {:state selector-state + :network-name (:network-name network) + :color color + :networks selected-networks + :label-props (get balance-per-chain (:chain-id network)) + :on-change #(rf/dispatch + [:wallet/update-selected-networks + (:network-name + network)])})) + layer-2-networks)}]])) diff --git a/src/status_im/contexts/wallet/sheets/network_preferences/view.cljs b/src/status_im/contexts/wallet/sheets/network_preferences/view.cljs index 5ef37d2bc3..f04e051f9e 100644 --- a/src/status_im/contexts/wallet/sheets/network_preferences/view.cljs +++ b/src/status_im/contexts/wallet/sheets/network_preferences/view.cljs @@ -4,31 +4,37 @@ [quo.theme :as quo.theme] [react-native.blur :as blur] [reagent.core :as reagent] + [status-im.constants :as constants] [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.sheets.network-preferences.style :as style] [utils.i18n :as i18n] [utils.re-frame :as rf])) (defn- view-internal - [{:keys [selected-networks watch-only?]}] + [{:keys [selected-networks account watch-only?]}] (let [state (reagent/atom :default) {:keys [color address - network-preferences-names]} (rf/sub [:wallet/current-viewing-account]) + network-preferences-names]} (or account (rf/sub [:wallet/current-viewing-account])) initial-network-preferences-names (or selected-networks network-preferences-names) network-preferences-names-state (reagent/atom #{}) toggle-network (fn [network-name] (reset! state :changed) - (if (contains? @network-preferences-names-state - network-name) - (swap! network-preferences-names-state disj - network-name) - (swap! network-preferences-names-state conj - network-name))) + (let [contains-network? (contains? + @network-preferences-names-state + network-name) + update-fn (if contains-network? disj conj) + networks-count (count + @network-preferences-names-state)] + (if (and (= networks-count 1) contains-network?) + (reset! network-preferences-names-state + (set constants/default-network-names)) + (swap! network-preferences-names-state update-fn + network-name)))) get-current-preferences-names (fn [] (if (= @state :default) initial-network-preferences-names @network-preferences-names-state))] - (fn [{:keys [on-save blur? theme]}] + (fn [{:keys [on-save blur? theme button-label]}] (let [network-details (rf/sub [:wallet/network-details]) mainnet (first network-details) layer-2-networks (rest network-details) @@ -70,30 +76,30 @@ [quo/category {:list-type :settings :blur? blur? - :data [(utils/make-network-item mainnet - {:state @state - :title (i18n/label :t/mainnet) - :color color - :blur? blur? - :networks (get-current-preferences-names) - :on-change #(toggle-network (:network-name - mainnet))})]}] + :data [(utils/make-network-item {:state @state + :network-name (:network-name mainnet) + :color color + :blur? blur? + :networks (get-current-preferences-names) + :on-change #(toggle-network (:network-name + mainnet))})]}] [quo/category {:list-type :settings :blur? blur? :label (i18n/label :t/layer-2) :data (mapv (fn [network] - (utils/make-network-item network - {:state @state - :color color - :blur? blur? - :networks (get-current-preferences-names) - :on-change #(toggle-network (:network-name - network))})) + (utils/make-network-item {:state @state + :network-name (:network-name network) + :color color + :blur? blur? + :networks (get-current-preferences-names) + :on-change #(toggle-network (:network-name + network))})) layer-2-networks)}] [quo/bottom-actions {:actions :one-action - :button-one-label (i18n/label :t/display) + :blur? blur? + :button-one-label (or button-label (i18n/label :t/update)) :button-one-props {:disabled? (= @state :default) :on-press (fn [] (let [chain-ids (map :chain-id current-networks)] diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 9034119f53..98628fddb6 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -2,7 +2,8 @@ (:require [legacy.status-im.fleet.core :as fleet] [react-native.core :as rn] - [status-im.contexts.shell.activity-center.events :as activity-center])) + [status-im.contexts.shell.activity-center.events :as activity-center] + [status-im.contexts.wallet.db :as wallet])) ;; initial state of app-db (def app-db @@ -18,6 +19,7 @@ :sync-state :done :link-previews-whitelist [] :app-state "active" + :wallet wallet/defaults :peers-count 0 :node-info {} :peers-summary [] diff --git a/src/status_im/feature_flags.cljs b/src/status_im/feature_flags.cljs index dd8d14b4f5..d6403625c8 100644 --- a/src/status_im/feature_flags.cljs +++ b/src/status_im/feature_flags.cljs @@ -13,7 +13,6 @@ {::wallet.bridge-token (enabled-in-env? :FLAG_BRIDGE_TOKEN_ENABLED) ::wallet.edit-derivation-path (enabled-in-env? :FLAG_EDIT_DERIVATION_PATH) ::wallet.remove-account (enabled-in-env? :FLAG_REMOVE_ACCOUNT_ENABLED) - ::wallet.network-filter (enabled-in-env? :FLAG_NETWORK_FILTER_ENABLED) ::community.edit-account-selection (enabled-in-env? :FLAG_EDIT_ACCOUNT_SELECTION_ENABLED)})) (defn feature-flags [] @feature-flags-config) diff --git a/src/status_im/subs/wallet/collectibles.cljs b/src/status_im/subs/wallet/collectibles.cljs index 4016bc2280..d04ddfb4f9 100644 --- a/src/status_im/subs/wallet/collectibles.cljs +++ b/src/status_im/subs/wallet/collectibles.cljs @@ -3,6 +3,10 @@ [clojure.string :as string] [re-frame.core :as re-frame])) +(defn- filter-collectibles-in-chains + [collectibles chain-ids] + (filter #(contains? chain-ids (get-in % [:id :contract-id :chain-id])) collectibles)) + (defn- svg-animation? [url media-type] (and (not (string/blank? url)) @@ -44,6 +48,13 @@ (fn [current-account] (-> current-account :collectibles add-collectibles-preview-url))) +(re-frame/reg-sub + :wallet/current-viewing-account-collectibles-in-selected-networks + :<- [:wallet/current-viewing-account-collectibles] + :<- [:wallet/selected-networks->chain-ids] + (fn [[collectibles chain-ids]] + (filter-collectibles-in-chains collectibles chain-ids))) + (re-frame/reg-sub :wallet/all-collectibles-list :<- [:wallet] @@ -61,6 +72,13 @@ (remove nil?) (add-collectibles-preview-url))))) +(re-frame/reg-sub + :wallet/all-collectibles-list-in-selected-networks + :<- [:wallet/all-collectibles-list] + :<- [:wallet/selected-networks->chain-ids] + (fn [[all-collectibles chain-ids]] + (filter-collectibles-in-chains all-collectibles chain-ids))) + (re-frame/reg-sub :wallet/current-viewing-account-collectibles-filtered :<- [:wallet/current-viewing-account-collectibles] diff --git a/src/status_im/subs/wallet/networks.cljs b/src/status_im/subs/wallet/networks.cljs index 017c048ecb..bbda3035b4 100644 --- a/src/status_im/subs/wallet/networks.cljs +++ b/src/status_im/subs/wallet/networks.cljs @@ -35,17 +35,17 @@ (defn get-network-details [chain-id] - (case chain-id - (constants/ethereum-mainnet-chain-id constants/ethereum-goerli-chain-id - constants/ethereum-sepolia-chain-id) + (condp contains? chain-id + #{constants/ethereum-mainnet-chain-id constants/ethereum-goerli-chain-id + constants/ethereum-sepolia-chain-id} mainnet-network-details - (constants/arbitrum-mainnet-chain-id constants/arbitrum-goerli-chain-id - constants/arbitrum-sepolia-chain-id) + #{constants/arbitrum-mainnet-chain-id constants/arbitrum-goerli-chain-id + constants/arbitrum-sepolia-chain-id} arbitrum-network-details - (constants/optimism-mainnet-chain-id constants/optimism-goerli-chain-id - constants/optimism-sepolia-chain-id) + #{constants/optimism-mainnet-chain-id constants/optimism-goerli-chain-id + constants/optimism-sepolia-chain-id} optimism-network-details nil)) @@ -55,13 +55,12 @@ :<- [:wallet/networks-by-mode] (fn [networks] (->> networks - (keep - (fn [{:keys [chain-id related-chain-id layer test?]}] - (let [network-details (get-network-details (if test? related-chain-id chain-id))] - (assoc network-details - :chain-id chain-id - :related-chain-id related-chain-id - :layer layer)))) + (map + (fn [{:keys [chain-id related-chain-id layer]}] + (assoc (get-network-details chain-id) + :chain-id chain-id + :related-chain-id related-chain-id + :layer layer))) (sort-by (juxt :layer :short-name))))) (re-frame/reg-sub @@ -69,3 +68,12 @@ :<- [:wallet/network-details] (fn [networks [_ chain-id]] (some #(when (= chain-id (:chain-id %)) %) networks))) + +(re-frame/reg-sub + :wallet/selected-network-details + :<- [:wallet/network-details] + :<- [:wallet/selected-networks] + (fn [[network-details selected-networks]] + (filter + #(contains? selected-networks (:network-name %)) + network-details))) diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs index c8bfccba64..8a33e4931c 100644 --- a/src/status_im/subs/wallet/wallet.cljs +++ b/src/status_im/subs/wallet/wallet.cljs @@ -44,11 +44,32 @@ :<- [:wallet/ui] :-> :create-account) +(rf/reg-sub + :wallet/network-filter + :<- [:wallet/ui] + :-> :network-filter) + +(rf/reg-sub + :wallet/selected-networks + :<- [:wallet/network-filter] + :-> :selected-networks) + +(rf/reg-sub + :wallet/network-filter-selector-state + :<- [:wallet/network-filter] + :-> :selector-state) + (rf/reg-sub :wallet/current-viewing-account-address :<- [:wallet] :-> :current-viewing-account-address) +(rf/reg-sub + :wallet/viewing-account? + :<- [:wallet/current-viewing-account-address] + (fn [address] + (boolean address))) + (rf/reg-sub :wallet/wallet-send-to-address :<- [:wallet/wallet-send] @@ -104,6 +125,18 @@ :<- [:wallet/create-account] :-> :selected-keypair-uid) +(rf/reg-sub + :wallet/selected-networks->chain-ids + :<- [:wallet/selected-networks] + :<- [:profile/test-networks-enabled?] + :<- [:profile/is-goerli-enabled?] + (fn [[selected-networks testnet-enabled? goerli-enabled?]] + (set (map #(utils/network->chain-id + {:network % + :testnet-enabled? testnet-enabled? + :goerli-enabled? goerli-enabled?}) + selected-networks)))) + (rf/reg-sub :wallet/accounts :<- [:wallet] @@ -131,17 +164,21 @@ set)) (rf/reg-sub - :wallet/balances + :wallet/balances-in-selected-networks :<- [:wallet/accounts] :<- [:profile/currency] - (fn [[accounts currency]] + :<- [:wallet/selected-networks->chain-ids] + (fn [[accounts currency chain-ids]] (zipmap (map :address accounts) - (map #(utils/calculate-balance-for-account currency %) accounts)))) + (map #(utils/calculate-balance-from-tokens {:currency currency + :tokens (:tokens %) + :chain-ids chain-ids}) + accounts)))) (rf/reg-sub :wallet/account-cards-data :<- [:wallet/accounts] - :<- [:wallet/balances] + :<- [:wallet/balances-in-selected-networks] :<- [:wallet/tokens-loading?] :<- [:profile/currency-symbol] (fn [[accounts balances tokens-loading? currency-symbol]] @@ -158,7 +195,7 @@ :wallet/current-viewing-account :<- [:wallet/accounts] :<- [:wallet/current-viewing-account-address] - :<- [:wallet/balances] + :<- [:wallet/balances-in-selected-networks] :<- [:profile/currency-symbol] (fn [[accounts current-viewing-account-address balances currency-symbol]] (let [balance (get balances current-viewing-account-address) @@ -169,15 +206,21 @@ :formatted-balance formatted-balance))))) (rf/reg-sub - :wallet/tokens-filtered + :wallet/current-viewing-account-tokens-in-selected-networks + :<- [:wallet/current-viewing-account] + :<- [:wallet/selected-networks->chain-ids] + (fn [[{:keys [tokens]} chain-ids]] + (utils/filter-tokens-in-chains tokens chain-ids))) + +(rf/reg-sub + :wallet/current-viewing-account-tokens-filtered :<- [:wallet/current-viewing-account] :<- [:wallet/network-details] (fn [[account networks] [_ query]] (let [tokens (map (fn [token] (assoc token - :networks (utils/network-list token networks) - :total-balance (utils/total-token-units-in-all-chains token) - :total-balance-fiat (utils/calculate-balance-for-token token))) + :networks (utils/network-list token networks) + :total-balance (utils/calculate-total-token-balance token))) (:tokens account)) sorted-tokens (sort-by :name compare tokens) filtered-tokens (filter #(or (string/starts-with? (string/lower-case (:name %)) @@ -194,9 +237,8 @@ (fn [[account networks] [_ token-symbol]] (let [tokens (map (fn [token] (assoc token - :networks (utils/network-list token networks) - :total-balance (utils/total-token-units-in-all-chains token) - :total-balance-fiat (utils/calculate-balance-for-token token))) + :networks (utils/network-list token networks) + :total-balance (utils/calculate-total-token-balance token))) (:tokens account)) token (first (filter #(= (string/lower-case (:symbol %)) (string/lower-case token-symbol)) @@ -214,14 +256,15 @@ :wallet/accounts-without-watched-accounts :<- [:wallet/accounts-with-customization-color] (fn [accounts] - (remove #(:watch-only? %) accounts))) + (remove :watch-only? accounts))) (rf/reg-sub - :wallet/account-token-values + :wallet/current-viewing-account-token-values :<- [:wallet/current-viewing-account] + :<- [:wallet/current-viewing-account-tokens-in-selected-networks] :<- [:profile/currency] :<- [:profile/currency-symbol] - (fn [[{:keys [tokens color]} currency currency-symbol]] + (fn [[{:keys [color]} tokens currency currency-symbol]] (mapv #(utils/calculate-token-value {:token % :color color :currency currency @@ -235,8 +278,15 @@ (utils/aggregate-tokens-for-all-accounts accounts))) (rf/reg-sub - :wallet/aggregated-tokens-and-balance + :wallet/aggregated-tokens-in-selected-networks :<- [:wallet/aggregated-tokens] + :<- [:wallet/selected-networks->chain-ids] + (fn [[aggregated-tokens chain-ids]] + (utils/filter-tokens-in-chains aggregated-tokens chain-ids))) + +(rf/reg-sub + :wallet/aggregated-token-values-and-balance + :<- [:wallet/aggregated-tokens-in-selected-networks] :<- [:profile/customization-color] :<- [:profile/currency] :<- [:profile/currency-symbol] @@ -287,3 +337,25 @@ :wallet/valid-ens-or-address? :<- [:wallet/search-address] :-> :valid-ens-or-address?) + +(rf/reg-sub + :wallet/aggregated-fiat-balance-per-chain + :<- [:wallet/aggregated-tokens] + :<- [:profile/currency] + :<- [:profile/currency-symbol] + (fn [[aggregated-tokens currency currency-symbol]] + (utils/calculate-balances-per-chain + {:tokens aggregated-tokens + :currency currency + :currency-symbol currency-symbol}))) + +(rf/reg-sub + :wallet/current-viewing-account-fiat-balance-per-chain + :<- [:wallet/current-viewing-account] + :<- [:profile/currency] + :<- [:profile/currency-symbol] + (fn [[{:keys [tokens]} currency currency-symbol]] + (utils/calculate-balances-per-chain + {:tokens tokens + :currency currency + :currency-symbol currency-symbol}))) diff --git a/src/status_im/subs/wallet/wallet_test.cljs b/src/status_im/subs/wallet/wallet_test.cljs index db271b87b6..05ec3b45cf 100644 --- a/src/status_im/subs/wallet/wallet_test.cljs +++ b/src/status_im/subs/wallet/wallet_test.cljs @@ -1,10 +1,13 @@ (ns status-im.subs.wallet.wallet-test - (:require [cljs.test :refer [is testing use-fixtures]] - [re-frame.db :as rf-db] - [status-im.subs.root] - [test-helpers.unit :as h] - [utils.money :as money] - [utils.re-frame :as rf])) + (:require + [cljs.test :refer [is testing use-fixtures]] + [re-frame.db :as rf-db] + [status-im.constants :as constants] + [status-im.contexts.wallet.db :as db] + [status-im.subs.root] + [test-helpers.unit :as h] + [utils.money :as money] + [utils.re-frame :as rf])) (use-fixtures :each {:before #(reset! rf-db/app-db {})}) @@ -13,47 +16,75 @@ [{:decimals 1 :symbol "ETH" :name "Ether" - :balances-per-chain {1 {:raw-balance (money/bignumber "20") :has-error false} - 2 {:raw-balance (money/bignumber "10") :has-error false}} + :balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber "20") + :has-error false} + constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber "10") + :has-error false}} :market-values-per-currency {:usd {:price 1000}}} {:decimals 2 :symbol "DAI" :name "Dai Stablecoin" - :balances-per-chain {1 {:raw-balance (money/bignumber "100") :has-error false} - 2 {:raw-balance (money/bignumber "150") :has-error false} - 3 {:raw-balance nil :has-error false}} + :balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber + "100") + :has-error false} + constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber + "150") + :has-error false} + constants/arbitrum-mainnet-chain-id {:raw-balance nil :has-error false}} :market-values-per-currency {:usd {:price 100}}}]) (def tokens-0x2 [{:decimals 3 :symbol "ETH" :name "Ether" - :balances-per-chain {1 {:raw-balance (money/bignumber "2500") :has-error false} - 2 {:raw-balance (money/bignumber "3000") :has-error false} - 3 {:raw-balance (money/bignumber "") :has-error false}} + :balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber + "2500") + :has-error false} + constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber + "3000") + :has-error false} + constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber + "") + :has-error false}} :market-values-per-currency {:usd {:price 200}}} {:decimals 10 :symbol "DAI" :name "Dai Stablecoin" - :balances-per-chain {1 {:raw-balance (money/bignumber "10000000000") :has-error false} - 2 {:raw-balance (money/bignumber "0") :has-error false} - 3 {:raw-balance (money/bignumber "") :has-error false}} + :balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber + "10000000000") + :has-error false} + constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber "0") + :has-error false} + constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber + "") + :has-error false}} :market-values-per-currency {:usd {:price 1000}}}]) (def tokens-0x3 [{:decimals 3 :symbol "ETH" :name "Ether" - :balances-per-chain {1 {:raw-balance (money/bignumber "5000") :has-error false} - 2 {:raw-balance (money/bignumber "2000") :has-error false} - 3 {:raw-balance (money/bignumber "") :has-error false}} + :balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber + "5000") + :has-error false} + constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber + "2000") + :has-error false} + constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber + "") + :has-error false}} :market-values-per-currency {:usd {:price 200}}} {:decimals 10 :symbol "DAI" :name "Dai Stablecoin" - :balances-per-chain {1 {:raw-balance (money/bignumber "10000000000") :has-error false} - 2 {:raw-balance (money/bignumber "0") :has-error false} - 3 {:raw-balance (money/bignumber "") :has-error false}} + :balances-per-chain {constants/ethereum-mainnet-chain-id {:raw-balance (money/bignumber + "10000000000") + :has-error false} + constants/optimism-mainnet-chain-id {:raw-balance (money/bignumber "0") + :has-error false} + constants/arbitrum-mainnet-chain-id {:raw-balance (money/bignumber + "") + :has-error false}} :market-values-per-currency {:usd {:price 1000}}}]) (def accounts @@ -148,10 +179,12 @@ :chain-id 10 :layer 2}]}) -(h/deftest-sub :wallet/balances +(h/deftest-sub :wallet/balances-in-selected-networks [sub-name] (testing "a map: address->balance" - (swap! rf-db/app-db #(assoc-in % [:wallet :accounts] accounts)) + (swap! rf-db/app-db #(-> % + (assoc :wallet db/defaults) + (assoc-in [:wallet :accounts] accounts))) (let [result (rf/sub [sub-name]) balance-0x1 (money/bignumber 3250) balance-0x2 (money/bignumber 2100) @@ -166,6 +199,7 @@ (testing "returns all accounts without balance" (swap! rf-db/app-db #(-> % + (assoc :wallet db/defaults) (assoc-in [:wallet :accounts] accounts) (assoc-in [:wallet :networks] network-data))) (is @@ -250,6 +284,7 @@ (testing "returns current account with balance base" (swap! rf-db/app-db #(-> % + (assoc :wallet db/defaults) (assoc-in [:wallet :accounts] accounts) (assoc-in [:wallet :current-viewing-account-address] "0x1") (assoc-in [:wallet :networks] network-data))) @@ -462,10 +497,12 @@ (is (match? 2 (count result))) (is (money/equal-to (money/bignumber 7520) eth-mainnet-raw-balance))))) -(h/deftest-sub :wallet/aggregated-tokens-and-balance +(h/deftest-sub :wallet/aggregated-token-values-and-balance [sub-name] (testing "returns aggregated tokens (in quo/token-value props) and balances from all accounts" - (swap! rf-db/app-db #(assoc-in % [:wallet :accounts] accounts)) + (swap! rf-db/app-db #(-> % + (assoc :wallet db/defaults) + (assoc-in [:wallet :accounts] accounts))) (let [{:keys [formatted-balance tokens]} (rf/sub [sub-name])] (is (match? 2 (count tokens))) (is (match? "$4506.00" formatted-balance))))) @@ -542,3 +579,99 @@ (swap! rf-db/app-db #(assoc-in % [:wallet :ui :create-account :selected-keypair-uid] "key-uid")) (is (= "key-uid" (rf/sub [sub-name]))))) + +(h/deftest-sub :wallet/current-viewing-account-tokens-filtered + [sub-name] + (testing "current viewing tokens filtered" + (swap! rf-db/app-db + #(-> % + (assoc-in [:wallet :accounts] accounts) + (assoc-in [:wallet :networks] network-data) + (assoc-in [:wallet :current-viewing-account-address] "0x2") + (assoc-in [:profile/profile :currency] :usd))) + (is (match? (count (rf/sub [sub-name ""])) 2)) + (is (match? (count (rf/sub [sub-name "et"])) 1)))) + +(h/deftest-sub :wallet/selected-networks->chain-ids + [sub-name] + (testing "selected networks -> chain-ids - All networks" + (swap! rf-db/app-db #(assoc % :wallet db/defaults)) + (is + (match? (sort [constants/ethereum-mainnet-chain-id constants/arbitrum-mainnet-chain-id + constants/optimism-mainnet-chain-id]) + (sort (rf/sub [sub-name]))))) + (testing "selected networks -> chain-ids - specific network" + (swap! rf-db/app-db #(assoc-in % + [:wallet :ui :network-filter :selected-networks] + #{constants/optimism-network-name})) + (is + (match? (sort [constants/optimism-mainnet-chain-id]) + (sort (rf/sub [sub-name])))))) + + +(h/deftest-sub :wallet/current-viewing-account-tokens-in-selected-networks + [sub-name] + (testing "current account tokens in selected networks" + (swap! rf-db/app-db + #(-> % + (assoc-in [:wallet :ui :network-filter :selected-networks] #{constants/arbitrum-network-name}) + (assoc-in [:wallet :accounts] accounts) + (assoc-in [:wallet :current-viewing-account-address] "0x1") + (assoc-in [:wallet :networks] network-data))) + + (let [result (rf/sub [sub-name]) + token (nth result 1) + chains (-> token + :balances-per-chain + keys)] + (is (match? (count chains) 1)) + (is (match? (first chains) constants/arbitrum-mainnet-chain-id))))) + +(h/deftest-sub :wallet/aggregated-tokens-in-selected-networks + [sub-name] + (testing "aggregated tokens in selected networks" + (swap! rf-db/app-db + #(-> % + (assoc-in [:wallet :ui :network-filter :selected-networks] #{constants/optimism-network-name}) + (assoc-in [:wallet :accounts] accounts) + (assoc-in [:wallet :networks] network-data))) + + (let [result (rf/sub [sub-name]) + token (first result) + chains (-> token + :balances-per-chain + keys)] + (is (match? (count chains) 1)) + (is (match? (first chains) constants/optimism-mainnet-chain-id))))) + +(h/deftest-sub :wallet/aggregated-fiat-balance-per-chain + [sub-name] + (testing "aggregated fiat balance per chain" + (swap! rf-db/app-db + #(-> % + (assoc-in [:wallet :accounts] accounts) + (assoc-in [:wallet :networks] network-data) + (assoc-in [:profile/profile :currency] :usd))) + + (let [result (rf/sub [sub-name]) + chains (keys result)] + (is (match? (count chains) 3)) + (is (match? (get result constants/ethereum-mainnet-chain-id) "$3504.00")) + (is (match? (get result constants/optimism-mainnet-chain-id) "$1002.00"))))) + +(h/deftest-sub :wallet/current-viewing-account-fiat-balance-per-chain + [sub-name] + (testing "current viewing account fiat balance per chain" + (swap! rf-db/app-db + #(-> % + (assoc-in [:wallet :accounts] accounts) + (assoc-in [:wallet :networks] network-data) + (assoc-in [:wallet :current-viewing-account-address] "0x2") + (assoc-in [:profile/profile :currency] :usd))) + + (let [result (rf/sub [sub-name]) + chains (keys result)] + (is (match? (count chains) 3)) + (is (match? (get result constants/ethereum-mainnet-chain-id) "$1500.00")) + (is (match? (get result constants/optimism-mainnet-chain-id) "$600.00")) + (is (match? (get result constants/arbitrum-mainnet-chain-id) "$0.00")))))