[Feature] Wallet - Network based assets and fiat balance calculation (#19150)

This commit:

(UI changes)

- adds the feature to filter assets (tokens and collectibles) and balances based on networks
- fixes network color for eth not shown on the multichain address (e.g. Account Options bottom sheet)
- fixes blur type in the confirm button in the network preferences sheet
- fixes the User ability to unselect all networks in the network preferences sheet
- fixes account address and color not shown in network-preferences sheet on Shell > Share Multichain address
- added STT token image

(Non-UI changes)

- Refactors the functions used for balance calculations and network details


Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit is contained in:
Mohamed Javid 2024-04-05 17:18:35 +05:30 committed by GitHub
parent 5fc23816a6
commit 9ba6c6262e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 675 additions and 350 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -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 ":")

View File

@ -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))]))

View File

@ -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]
[:<>])])

View File

@ -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}]]

View File

@ -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 []

View File

@ -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)]

View File

@ -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}}])))

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"])

View File

@ -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])}])))])

View File

@ -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 %))))

View File

@ -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))))

View File

@ -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}})

View File

@ -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)

View File

@ -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)))))

View File

@ -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

View File

@ -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}

View File

@ -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 %)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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]}]

View File

@ -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)}]]))

View File

@ -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)]

View File

@ -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 []

View File

@ -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)

View File

@ -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]

View File

@ -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)))

View File

@ -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})))

View File

@ -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 "<nil>") :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
"<nil>")
: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 "<nil>") :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
"<nil>")
: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 "<nil>") :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
"<nil>")
: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 "<nil>") :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
"<nil>")
: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")))))