Show aggregated tokens and balance in Wallet home (#18275)
This commit adds the feature to show aggregated tokens and balances in the Wallet home. --------- Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit is contained in:
parent
b4640dabb9
commit
a00e44d056
|
@ -7,7 +7,6 @@
|
|||
[status-im.contexts.wallet.account.tabs.view :as tabs]
|
||||
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
|
||||
[status-im.contexts.wallet.common.temp :as temp]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
@ -35,12 +34,12 @@
|
|||
[]
|
||||
(let [selected-tab (reagent/atom first-tab-id)]
|
||||
(fn []
|
||||
(let [{:keys [name color balance watch-only?]} (rf/sub [:wallet/current-viewing-account])
|
||||
currency-symbol (rf/sub [:profile/currency-symbol])]
|
||||
(let [{:keys [name color formatted-balance
|
||||
watch-only?]} (rf/sub [:wallet/current-viewing-account])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[account-switcher/view {:on-press #(rf/dispatch [:wallet/close-account-page])}]
|
||||
[quo/account-overview
|
||||
{:current-value (utils/prettify-balance currency-symbol balance)
|
||||
{:current-value formatted-balance
|
||||
:account-name name
|
||||
:account (if watch-only? :watched-address :default)
|
||||
:customization-color color}]
|
||||
|
|
|
@ -4,35 +4,6 @@
|
|||
[status-im.common.resources :as status.resources]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(def tokens
|
||||
[{:token :snt
|
||||
:token-name "Status"
|
||||
:state :default
|
||||
:status :empty
|
||||
:customization-color :blue
|
||||
:values {:crypto-value "0.00"
|
||||
:fiat-value "€0.00"
|
||||
:percentage-change "0.00"
|
||||
:fiat-change "€0.00"}}
|
||||
{:token :eth
|
||||
:token-name "Ether"
|
||||
:state :default
|
||||
:status :empty
|
||||
:customization-color :blue
|
||||
:values {:crypto-value "0.00"
|
||||
:fiat-value "€0.00"
|
||||
:percentage-change "0.00"
|
||||
:fiat-change "€0.00"}}
|
||||
{:token :dai
|
||||
:token-name "Dai Stablecoin"
|
||||
:state :default
|
||||
:status :empty
|
||||
:customization-color :blue
|
||||
:values {:crypto-value "0.00"
|
||||
:fiat-value "€0.00"
|
||||
:percentage-change "0.00"
|
||||
:fiat-change "€0.00"}}])
|
||||
|
||||
(def address "0x39cf6E0Ba4C4530735616e1Ee7ff5FbCB726fBd4")
|
||||
|
||||
(def buy-tokens-list
|
||||
|
|
|
@ -87,18 +87,6 @@
|
|||
[accounts address]
|
||||
(some #(when (= (:address %) address) %) accounts))
|
||||
|
||||
(defn calculate-raw-balance
|
||||
[raw-balance decimals]
|
||||
(if-let [n (utils.number/parse-int raw-balance nil)]
|
||||
(/ n (Math/pow 10 (utils.number/parse-int decimals)))
|
||||
0))
|
||||
|
||||
(defn token-value-in-chain
|
||||
[{:keys [balances-per-chain decimals]} chain-id]
|
||||
(let [balance-in-chain (get balances-per-chain chain-id)]
|
||||
(when balance-in-chain
|
||||
(calculate-raw-balance (:raw-balance balance-in-chain) decimals))))
|
||||
|
||||
(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}]
|
||||
|
@ -127,6 +115,41 @@
|
|||
(map #(calculate-balance-for-token %))
|
||||
(reduce +)))
|
||||
|
||||
(defn calculate-balance-from-tokens
|
||||
[{:keys [currency tokens]}]
|
||||
(->> tokens
|
||||
(map #(total-token-fiat-value currency %))
|
||||
(reduce money/add)))
|
||||
|
||||
(defn- add-balances-per-chain
|
||||
[b1 b2]
|
||||
{:raw-balance (money/add (:raw-balance b1) (:raw-balance b2))
|
||||
:chain-id (:chain-id b2)})
|
||||
|
||||
(defn- merge-token
|
||||
[existing-token token]
|
||||
(assoc token
|
||||
:balances-per-chain
|
||||
(merge-with add-balances-per-chain
|
||||
(:balances-per-chain existing-token)
|
||||
(:balances-per-chain token))))
|
||||
|
||||
(defn aggregate-tokens-for-all-accounts
|
||||
"Receives accounts (seq) and returns aggregated tokens in all accounts
|
||||
NOTE: We use double reduce for faster performance (faster than mapcat and flatten)"
|
||||
[accounts]
|
||||
(->> accounts
|
||||
(map :tokens)
|
||||
(reduce
|
||||
(fn [result-map tokens-per-account]
|
||||
(reduce
|
||||
(fn [acc token]
|
||||
(update acc (:symbol token) merge-token token))
|
||||
result-map
|
||||
tokens-per-account))
|
||||
{})
|
||||
vals))
|
||||
|
||||
(defn network-list
|
||||
[{:keys [balances-per-chain]} networks]
|
||||
(into #{}
|
||||
|
@ -136,10 +159,6 @@
|
|||
networks)))
|
||||
(keys balances-per-chain))))
|
||||
|
||||
(defn calculate-fiat-change
|
||||
[fiat-value change-pct-24hour]
|
||||
(money/bignumber (* fiat-value (/ change-pct-24hour (+ 100 change-pct-24hour)))))
|
||||
|
||||
(defn get-wallet-qr
|
||||
[{:keys [wallet-type selected-networks address]}]
|
||||
(if (= wallet-type :wallet-multichain)
|
||||
|
@ -153,3 +172,28 @@
|
|||
{constants/mainnet-chain-id :ethereum
|
||||
constants/optimism-chain-id :optimism
|
||||
constants/arbitrum-chain-id :arbitrum})
|
||||
|
||||
(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)
|
||||
market-values (or (get-in token [:market-values-per-currency currency])
|
||||
(get-in token
|
||||
[:market-values-per-currency
|
||||
constants/profile-default-currency]))
|
||||
{:keys [change-pct-24hour]} market-values
|
||||
crypto-value (get-standard-crypto-format token token-units)
|
||||
fiat-value (if (string/includes? crypto-value "<")
|
||||
"<$0.01"
|
||||
(prettify-balance currency-symbol fiat-value))]
|
||||
{:token (:symbol token)
|
||||
:token-name (:name token)
|
||||
:state :default
|
||||
:status (cond
|
||||
(pos? change-pct-24hour) :positive
|
||||
(neg? change-pct-24hour) :negative
|
||||
:else :empty)
|
||||
:customization-color color
|
||||
:values {:crypto-value crypto-value
|
||||
:fiat-value fiat-value}}))
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[utils.money :as money]))
|
||||
|
||||
|
||||
(deftest test-get-first-name
|
||||
(testing "get-first-name function"
|
||||
(is (= (utils/get-first-name "John Doe") "John"))
|
||||
|
@ -93,22 +92,6 @@
|
|||
address-to-find "0x999"]
|
||||
(is (= (utils/get-account-by-address accounts address-to-find) nil)))))
|
||||
|
||||
(deftest test-calculate-raw-balance
|
||||
(testing "calculate-raw-balance function"
|
||||
(is (= (utils/calculate-raw-balance "100000000" "8") 1.0))
|
||||
(is (= (utils/calculate-raw-balance "50000000" "8") 0.5))
|
||||
(is (= (utils/calculate-raw-balance "123456789" "2") 1234567.89))
|
||||
(is (= (utils/calculate-raw-balance "0" "4") 0.0))))
|
||||
|
||||
(deftest test-token-value-in-chain
|
||||
(testing "token-value-in-chain function"
|
||||
(let [token {:balances-per-chain {1 {:raw-balance (money/bignumber 100000000)}
|
||||
2 {:raw-balance (money/bignumber 50000000)}
|
||||
3 {:raw-balance (money/bignumber 123456789)}}
|
||||
:decimals 8}]
|
||||
(is (= (utils/token-value-in-chain token 1) 1.0)))))
|
||||
|
||||
|
||||
(deftest test-get-wallet-qr
|
||||
(testing "Test get-wallet-qr function"
|
||||
(let [wallet-multichain {:wallet-type :wallet-multichain
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
(ns status-im.contexts.wallet.data-store
|
||||
(:require
|
||||
[camel-snake-kebab.core :as csk]
|
||||
[camel-snake-kebab.extras :as cske]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[status-im.constants :as constants]
|
||||
[utils.money :as money]
|
||||
[utils.number :as utils.number]))
|
||||
|
||||
(defn chain-ids-string->set
|
||||
|
@ -48,6 +51,19 @@
|
|||
(update :testPreferredChainIds chain-ids-set->string)
|
||||
(dissoc :watch-only?)))
|
||||
|
||||
(defn- rpc->balances-per-chain
|
||||
[token]
|
||||
(-> token
|
||||
(update :balances-per-chain update-vals #(update % :raw-balance money/bignumber))
|
||||
(update :balances-per-chain update-keys (comp utils.number/parse-int name))))
|
||||
|
||||
(defn rpc->tokens
|
||||
[tokens]
|
||||
(-> tokens
|
||||
(update-keys name)
|
||||
(update-vals #(cske/transform-keys csk/->kebab-case %))
|
||||
(update-vals #(mapv rpc->balances-per-chain %))))
|
||||
|
||||
(defn <-rpc
|
||||
[network]
|
||||
(-> network
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
(ns status-im.contexts.wallet.events
|
||||
(:require
|
||||
[camel-snake-kebab.core :as csk]
|
||||
[camel-snake-kebab.extras :as cske]
|
||||
[clojure.string :as string]
|
||||
[react-native.background-timer :as background-timer]
|
||||
[status-im.contexts.wallet.data-store :as data-store]
|
||||
|
@ -12,7 +10,6 @@
|
|||
[utils.ethereum.chain :as chain]
|
||||
[utils.ethereum.eip.eip55 :as eip55]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.money :as money]
|
||||
[utils.number]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
|
@ -109,19 +106,10 @@
|
|||
:params params})
|
||||
{:db (assoc-in db [:wallet :ui :tokens-loading?] false)}))
|
||||
|
||||
(defn- fix-balances-per-chain
|
||||
[token]
|
||||
(-> token
|
||||
(update :balances-per-chain update-vals #(update % :raw-balance money/bignumber))
|
||||
(update :balances-per-chain update-keys (comp utils.number/parse-int name))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet/store-wallet-token
|
||||
(fn [{:keys [db]} [raw-tokens-data]]
|
||||
(let [tokens (-> raw-tokens-data
|
||||
(update-keys name)
|
||||
(update-vals #(cske/transform-keys csk/->kebab-case %))
|
||||
(update-vals #(mapv fix-balances-per-chain %)))
|
||||
(let [tokens (data-store/rpc->tokens raw-tokens-data)
|
||||
add-tokens (fn [stored-accounts tokens-per-account]
|
||||
(reduce-kv (fn [accounts address tokens-data]
|
||||
(if (accounts address)
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
(:require
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[status-im.contexts.wallet.common.temp :as temp]
|
||||
[status-im.contexts.wallet.home.tabs.assets.style :as style]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
(let [tokens-loading? (rf/sub [:wallet/tokens-loading?])]
|
||||
(let [tokens-loading? (rf/sub [:wallet/tokens-loading?])
|
||||
{:keys [tokens]} (rf/sub [:wallet/aggregated-tokens-and-balance])]
|
||||
(if tokens-loading?
|
||||
[quo/skeleton-list
|
||||
{:content :assets
|
||||
|
@ -16,6 +16,5 @@
|
|||
:animated? false}]
|
||||
[rn/flat-list
|
||||
{:render-fn quo/token-value
|
||||
:data temp/tokens
|
||||
:key :assets-list
|
||||
:data tokens
|
||||
:content-container-style style/list-container}])))
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
(let [tokens-loading? (rf/sub [:wallet/tokens-loading?])
|
||||
networks (rf/sub [:wallet/network-details])
|
||||
account-cards-data (rf/sub [:wallet/account-cards-data])
|
||||
cards (conj account-cards-data (new-account-card-data))]
|
||||
cards (conj account-cards-data (new-account-card-data))
|
||||
{:keys [formatted-balance]} (rf/sub [:wallet/aggregated-tokens-and-balance])]
|
||||
[rn/view {:style (style/home-container)}
|
||||
[common.top-nav/view]
|
||||
[rn/view {:style style/overview-container}
|
||||
|
@ -50,7 +51,7 @@
|
|||
{:state (if tokens-loading? :loading :default)
|
||||
:time-frame :none
|
||||
:metrics :none
|
||||
:balance "€0.00"
|
||||
:balance formatted-balance
|
||||
:networks networks}]]
|
||||
[quo/wallet-graph {:time-frame :empty}]
|
||||
[rn/flat-list
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
(ns status-im.subs.wallet.wallet
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as rf]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.common.utils :as utils]
|
||||
[utils.number]))
|
||||
|
||||
|
@ -120,10 +119,14 @@
|
|||
:<- [:wallet/accounts]
|
||||
:<- [:wallet/current-viewing-account-address]
|
||||
:<- [:wallet/balances]
|
||||
(fn [[accounts current-viewing-account-address balances]]
|
||||
(let [current-viewing-account (utils/get-account-by-address accounts current-viewing-account-address)]
|
||||
:<- [:profile/currency-symbol]
|
||||
(fn [[accounts current-viewing-account-address balances currency-symbol]]
|
||||
(let [current-viewing-account (utils/get-account-by-address accounts current-viewing-account-address)
|
||||
balance (get balances current-viewing-account-address)
|
||||
formatted-balance (utils/prettify-balance currency-symbol balance)]
|
||||
(-> current-viewing-account
|
||||
(assoc :balance (get balances current-viewing-account-address))))))
|
||||
(assoc :balance balance
|
||||
:formatted-balance formatted-balance)))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/tokens-filtered
|
||||
|
@ -157,37 +160,41 @@
|
|||
(fn [accounts]
|
||||
(remove #(:watch-only? %) accounts)))
|
||||
|
||||
(defn- calc-token-value
|
||||
[{:keys [market-values-per-currency] :as token} color currency currency-symbol]
|
||||
(let [token-units (utils/total-token-units-in-all-chains token)
|
||||
fiat-value (utils/total-token-fiat-value currency token)
|
||||
market-values (get market-values-per-currency
|
||||
currency
|
||||
(get market-values-per-currency
|
||||
constants/profile-default-currency))
|
||||
{:keys [change-pct-24hour]} market-values
|
||||
crypto-value (utils/get-standard-crypto-format token token-units)
|
||||
fiat-value (if (string/includes? crypto-value "<")
|
||||
"<$0.01"
|
||||
(utils/prettify-balance currency-symbol fiat-value))]
|
||||
{:token (:symbol token)
|
||||
:token-name (:name token)
|
||||
:state :default
|
||||
:status (cond
|
||||
(pos? change-pct-24hour) :positive
|
||||
(neg? change-pct-24hour) :negative
|
||||
:else :empty)
|
||||
:customization-color color
|
||||
:values {:crypto-value crypto-value
|
||||
:fiat-value fiat-value}}))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/account-token-values
|
||||
:<- [:wallet/current-viewing-account]
|
||||
:<- [:profile/currency]
|
||||
:<- [:profile/currency-symbol]
|
||||
(fn [[{:keys [tokens color]} currency currency-symbol]]
|
||||
(mapv #(calc-token-value % color currency currency-symbol) tokens)))
|
||||
(mapv #(utils/calculate-token-value {:token %
|
||||
:color color
|
||||
:currency currency
|
||||
:currency-symbol currency-symbol})
|
||||
tokens)))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/aggregated-tokens
|
||||
:<- [:wallet/accounts]
|
||||
(fn [accounts]
|
||||
(utils/aggregate-tokens-for-all-accounts accounts)))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/aggregated-tokens-and-balance
|
||||
:<- [:wallet/aggregated-tokens]
|
||||
:<- [:profile/customization-color]
|
||||
:<- [:profile/currency]
|
||||
:<- [:profile/currency-symbol]
|
||||
(fn [[aggregated-tokens color currency currency-symbol]]
|
||||
(let [balance (utils/calculate-balance-from-tokens {:currency currency
|
||||
:tokens aggregated-tokens})
|
||||
formatted-balance (utils/prettify-balance currency-symbol balance)]
|
||||
{:balance balance
|
||||
:formatted-balance formatted-balance
|
||||
:tokens (mapv #(utils/calculate-token-value {:token %
|
||||
:color color
|
||||
:currency currency
|
||||
:currency-symbol currency-symbol})
|
||||
aggregated-tokens)})))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/network-preference-details
|
||||
|
|
|
@ -278,9 +278,10 @@
|
|||
:public-key "0x04371e2d9d66b82f056bc128064"
|
||||
:removed false
|
||||
:tokens tokens-0x1}
|
||||
(dissoc result :balance)))
|
||||
(dissoc result :balance :formatted-balance)))
|
||||
|
||||
(is (money/equal-to (:balance result) (money/bignumber 3250))))))
|
||||
(is (money/equal-to (:balance result) (money/bignumber 3250)))
|
||||
(is (match? (:formatted-balance result) "$3250.00")))))
|
||||
|
||||
(h/deftest-sub :wallet/addresses
|
||||
[sub-name]
|
||||
|
@ -440,3 +441,21 @@
|
|||
(->> (rf/sub [sub-name])
|
||||
;; Removed `#js source` property for correct compare
|
||||
(map #(dissoc % :source)))))))
|
||||
|
||||
(h/deftest-sub :wallet/aggregated-tokens
|
||||
[sub-name]
|
||||
(testing "returns aggregated tokens from all accounts"
|
||||
(swap! rf-db/app-db #(assoc-in % [:wallet :accounts] accounts))
|
||||
(let [result (rf/sub [sub-name])
|
||||
eth-token (some #(when (= (:symbol %) "ETH") %) result)
|
||||
eth-mainnet-raw-balance (get-in eth-token [:balances-per-chain 1 :raw-balance])]
|
||||
(is (match? 2 (count result)))
|
||||
(is (money/equal-to (money/bignumber 7520) eth-mainnet-raw-balance)))))
|
||||
|
||||
(h/deftest-sub :wallet/aggregated-tokens-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))
|
||||
(let [{:keys [formatted-balance tokens]} (rf/sub [sub-name])]
|
||||
(is (match? 2 (count tokens)))
|
||||
(is (match? "$4506.00" formatted-balance)))))
|
||||
|
|
Loading…
Reference in New Issue