Skip to content

Commit

Permalink
[#17896] Raw balances using big number (#17920)
Browse files Browse the repository at this point in the history
* Add `utils.money/add` version able to work with `nil`s

* Store token raw-balance as big-number

* Improve account balance calculations using `utils.money` existing functions

* Update `:wallet/tokens-filtered` sub

* Make `prettify-balance` able to work with bignumbers
  • Loading branch information
ulisesmac authored and yevh-berdnyk committed Dec 8, 2023
1 parent b92c662 commit eea3c0e
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 75 deletions.
4 changes: 2 additions & 2 deletions src/quo/components/wallet/account_card/view.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
:end {:x 1 :y 0}}])

(defn- user-account
[]
[_]
(let [pressed? (reagent/atom false)
on-press-in #(reset! pressed? true)
on-press-out #(reset! pressed? false)]
Expand Down Expand Up @@ -147,7 +147,7 @@
[gradient-overview theme customization-color])])))))

(defn- add-account-view
[]
[_]
(let [pressed? (reagent/atom false)]
(fn [{:keys [on-press customization-color theme metrics?]}]
[rn/pressable
Expand Down
45 changes: 30 additions & 15 deletions src/status_im2/contexts/wallet/common/utils.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@

(defn prettify-balance
[balance]
(str "$" (.toFixed (if (number? balance) balance 0) 2)))
(let [valid-balance? (and balance
(or (number? balance) (.-toFixed balance)))]
(as-> balance $
(if valid-balance? $ 0)
(.toFixed $ 2)
(str "$" $))))

(defn get-derivation-path
[number-of-accounts]
Expand All @@ -25,32 +30,42 @@
(let [path (get-derivation-path number-of-accounts)]
(format-derivation-path path)))

(defn- total-raw-balance-in-all-chains
[balances-per-chain]
(->> balances-per-chain
(map (comp :raw-balance val))
(reduce money/add)))

(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 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 total-token-value-in-all-chains
[{:keys [balances-per-chain decimals]}]
(->> balances-per-chain
(vals)
(map #(calculate-raw-balance (:raw-balance %) decimals))
(reduce +)))

(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 calculate-balance
[tokens-in-account]
(->> tokens-in-account
(map (fn [token]
(* (total-token-value-in-all-chains token)
(-> token :market-values-per-currency :usd :price))))
(reduce +)))
(defn total-token-fiat-value
"Returns the total token fiat value taking into account all token's chains."
[{:keys [market-values-per-currency] :as token}]
(let [usd-price (-> market-values-per-currency :usd :price)
total-units-in-all-chains (total-token-units-in-all-chains token)]
(money/crypto->fiat total-units-in-all-chains usd-price)))

(defn calculate-balance-for-account
[{:keys [tokens] :as _account}]
(->> tokens
(map total-token-fiat-value)
(reduce money/add)))

(defn network-list
[{:keys [balances-per-chain]} networks]
Expand Down
9 changes: 6 additions & 3 deletions src/status_im2/contexts/wallet/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
[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]
[utils.transforms :as types]))
Expand Down Expand Up @@ -108,17 +109,19 @@
:event :wallet/get-wallet-token
:params addresses})}]]]})))

(defn- fix-chain-id-keys
(defn- fix-balances-per-chain
[token]
(update token :balances-per-chain update-keys (comp utils.number/parse-int name)))
(-> 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-chain-id-keys %)))
(update-vals #(mapv fix-balances-per-chain %)))
add-tokens (fn [stored-accounts tokens-per-account]
(reduce-kv (fn [accounts address tokens-data]
(if (accounts address)
Expand Down
29 changes: 13 additions & 16 deletions src/status_im2/subs/wallet/wallet.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
:<- [:wallet/accounts]
(fn [accounts]
(zipmap (map :address accounts)
(map #(-> % :tokens utils/calculate-balance) accounts))))
(map utils/calculate-balance-for-account accounts))))

(rf/reg-sub
:wallet/account-cards-data
Expand Down Expand Up @@ -71,21 +71,18 @@
:<- [: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-value-in-all-chains token)
:total-balance-fiat (utils/calculate-balance token)))
(:tokens account))

sorted-tokens
(sort-by :name compare tokens)
filtered-tokens
(filter #(or (string/starts-with? (string/lower-case (:name %))
(string/lower-case query))
(string/starts-with? (string/lower-case (:symbol %))
(string/lower-case query)))
sorted-tokens)]
(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 0))
(:tokens account))
sorted-tokens (sort-by :name compare tokens)
filtered-tokens (filter #(or (string/starts-with? (string/lower-case (:name %))
(string/lower-case query))
(string/starts-with? (string/lower-case (:symbol %))
(string/lower-case query)))
sorted-tokens)]
filtered-tokens)))

(rf/reg-sub
Expand Down
81 changes: 44 additions & 37 deletions src/status_im2/subs/wallet/wallet_test.cljs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
(ns status-im2.subs.wallet.wallet-test
(:require [cljs.test :refer [is testing use-fixtures]]
[re-frame.db :as rf-db]
status-im2.subs.root
[status-im2.subs.root]
[test-helpers.unit :as h]
[utils.money :as money]
[utils.re-frame :as rf]))

(use-fixtures :each
Expand All @@ -12,30 +13,31 @@
[{:decimals 1
:symbol "ETH"
:name "Ether"
:balances-per-chain {1 {:raw-balance "20" :has-error false}
2 {:raw-balance "10" :has-error false}}
:balances-per-chain {1 {:raw-balance (money/bignumber "20") :has-error false}
2 {: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 "100" :has-error false}
2 {:raw-balance "150" :has-error false}}
: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}}
:market-values-per-currency {:usd {:price 100}}}])

(def tokens-0x2
[{:decimals 3
:symbol "ETH"
:name "Ether"
:balances-per-chain {1 {:raw-balance "2500" :has-error false}
2 {:raw-balance "3000" :has-error false}
3 {:raw-balance "<nil>" :has-error false}}
: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}}
:market-values-per-currency {:usd {:price 200}}}
{:decimals 10
:symbol "DAI"
:name "Dai Stablecoin"
:balances-per-chain {1 {:raw-balance "10000000000" :has-error false}
2 {:raw-balance "0" :has-error false}
3 {:raw-balance "<nil>" :has-error false}}
: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}}
:market-values-per-currency {:usd {:price 1000}}}])

(def accounts
Expand Down Expand Up @@ -84,9 +86,12 @@
[sub-name]
(testing "a map: address->balance"
(swap! rf-db/app-db #(assoc-in % [:wallet :accounts] accounts))
(let [result (rf/sub [sub-name])
balance-0x1 (money/bignumber 3250)
balance-0x2 (money/bignumber 2100)]

(is (= {"0x1" 3250 "0x2" 2100}
(rf/sub [sub-name])))))
(is (money/equal-to balance-0x1 (get result "0x1")))
(is (money/equal-to balance-0x2 (get result "0x2"))))))

(h/deftest-sub :wallet/accounts
[sub-name]
Expand Down Expand Up @@ -143,30 +148,32 @@
#(-> %
(assoc-in [:wallet :accounts] accounts)
(assoc-in [:wallet :current-viewing-account-address] "0x1")))

(is
(= {:path "m/44'/60'/0'/0/0"
:emoji "😃"
:key-uid "0x2f5ea39"
:address "0x1"
:wallet false
:name "Account One"
:type :generated
:chat false
:test-preferred-chain-ids #{5 420 421613}
:color :blue
:hidden false
:prod-preferred-chain-ids #{1 10 42161}
:position 0
:clock 1698945829328
:created-at 1698928839000
:operable "fully"
:mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064"
:removed false
:balance 3250
:tokens tokens-0x1}
(rf/sub [sub-name])))))
(let [result (rf/sub [sub-name])]

(is
(= {:path "m/44'/60'/0'/0/0"
:emoji "😃"
:key-uid "0x2f5ea39"
:address "0x1"
:wallet false
:name "Account One"
:type :generated
:chat false
:test-preferred-chain-ids #{5 420 421613}
:color :blue
:hidden false
:prod-preferred-chain-ids #{1 10 42161}
:position 0
:clock 1698945829328
:created-at 1698928839000
:operable "fully"
:mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064"
:removed false
:tokens tokens-0x1}
(dissoc result :balance)))

(is (money/equal-to (:balance result) (money/bignumber 3250))))))


(h/deftest-sub :wallet/addresses
Expand Down
8 changes: 6 additions & 2 deletions src/utils/money.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
;; E.g. for Ether, it's smallest part is wei or 10^(-18) of 1 ether
;; for arbitrary ERC20 token the smallest part is 10^(-decimals) of 1 token
;;
;; Different tokens can have different number of allowed decimals, so it's neccessary to include the
;; Different tokens can have different number of allowed decimals, so it's necessary to include the
;; decimals parameter
;; to get the amount scale right.

Expand Down Expand Up @@ -205,10 +205,14 @@
(with-precision 2)
str))

(defn add
(defn- add*
[bn1 n2]
(.add ^js bn1 n2))

(def add
"Add with defaults, this version is able to receive `nil` and takes them as 0."
(fnil add* (bignumber 0) (bignumber 0)))

(defn mul
[bn1 bn2]
(.mul ^js bn1 bn2))
Expand Down

0 comments on commit eea3c0e

Please sign in to comment.