diff --git a/src/status_im/ethereum/decode.cljs b/src/status_im/ethereum/decode.cljs index 695f746f63..f64c594465 100644 --- a/src/status_im/ethereum/decode.cljs +++ b/src/status_im/ethereum/decode.cljs @@ -1,5 +1,5 @@ (ns status-im.ethereum.decode - (:require [status-im.utils.money :as money])) + (:require [utils.money :as money])) (defn uint [hex] diff --git a/src/status_im/ethereum/eip681.cljs b/src/status_im/ethereum/eip681.cljs index bdc6e83d3e..6c38fddb32 100644 --- a/src/status_im/ethereum/eip681.cljs +++ b/src/status_im/ethereum/eip681.cljs @@ -8,7 +8,7 @@ [status-im.ethereum.core :as ethereum] [status-im.ethereum.ens :as ens] [status-im.ethereum.tokens :as tokens] - [status-im.utils.money :as money])) + [utils.money :as money])) (def scheme "ethereum") (def scheme-separator ":") diff --git a/src/status_im/ethereum/eip681_test.cljs b/src/status_im/ethereum/eip681_test.cljs index 4bb94bf11a..e4d7a54062 100644 --- a/src/status_im/ethereum/eip681_test.cljs +++ b/src/status_im/ethereum/eip681_test.cljs @@ -1,7 +1,7 @@ (ns status-im.ethereum.eip681-test (:require [cljs.test :refer-macros [deftest is] :as test] [status-im.ethereum.eip681 :as eip681] - [status-im.utils.money :as money])) + [utils.money :as money])) (deftest parse-uri (is (= nil (eip681/parse-uri nil))) diff --git a/src/status_im/keycard/sign.cljs b/src/status_im/keycard/sign.cljs index 0d22368fd3..e16dff8ddb 100644 --- a/src/status_im/keycard/sign.cljs +++ b/src/status_im/keycard/sign.cljs @@ -4,7 +4,7 @@ [status-im.ethereum.core :as ethereum] [status-im.keycard.common :as common] [utils.re-frame :as rf] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.utils.types :as types] [taoensso.timbre :as log])) @@ -38,8 +38,8 @@ {:db (assoc-in db [:signing/sign :keycard-step] :signing)} (common/set-on-card-connected :keycard/sign)) - (pos? keycard-pin-retries) ; if 0, get-application-info will have already closed the connection - ; sheet and opened the frozen card popup + (pos? keycard-pin-retries) ; if 0, get-application-info will have already closed the + ; connection sheet and opened the frozen card popup {:db (-> db (assoc-in [:keycard :card-read-in-progress?] true) (assoc-in [:keycard :pin :status] :verifying)) diff --git a/src/status_im/notifications/local.cljs b/src/status_im/notifications/local.cljs index 1c1f918c98..31b49e8602 100644 --- a/src/status_im/notifications/local.cljs +++ b/src/status_im/notifications/local.cljs @@ -10,7 +10,7 @@ [utils.i18n :as i18n] [status-im.notifications.android :as pn-android] [utils.re-frame :as rf] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.utils.types :as types] [status-im.utils.utils :as utils] [react-native.core :as rn])) diff --git a/src/status_im/signing/core.cljs b/src/status_im/signing/core.cljs index 22e1e460de..7b2ba417ce 100644 --- a/src/status_im/signing/core.cljs +++ b/src/status_im/signing/core.cljs @@ -14,7 +14,7 @@ [status-im.signing.keycard :as signing.keycard] [utils.re-frame :as rf] [status-im.utils.hex :as utils.hex] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.utils.types :as types] [status-im.utils.utils :as utils] [status-im.wallet.core :as wallet] @@ -313,8 +313,8 @@ {:events [:sign/send-transaction-message]} [cofx chat-id value contract transaction-hash signature] {:json-rpc/call [{:method "wakuext_sendTransaction" - ;; We make sure `value` is serialized as string, and not - ;; as an integer or big-int + ;; We make sure `value` is serialized as string, and not as an integer or + ;; big-int :params [chat-id (str value) contract transaction-hash (or (:result (types/json->clj signature)) (ethereum/normalized-hex signature))] diff --git a/src/status_im/signing/gas.cljs b/src/status_im/signing/gas.cljs index 93cee0f01e..68644e80f0 100644 --- a/src/status_im/signing/gas.cljs +++ b/src/status_im/signing/gas.cljs @@ -7,7 +7,7 @@ [status-im.popover.core :as popover] [status-im.signing.eip1559 :as eip1559] [utils.re-frame :as rf] - [status-im.utils.money :as money] + [utils.money :as money] [status-im2.common.json-rpc.events :as json-rpc] [taoensso.timbre :as log])) @@ -336,7 +336,7 @@ (let [sorted-v (sort-by identity (fn [a b] - (status-im.utils.money/greater-than b a)) + (utils.money/greater-than b a)) v)] (reduce (fn [acc p] diff --git a/src/status_im/ui/screens/signing/sheets.cljs b/src/status_im/ui/screens/signing/sheets.cljs index 179c75c1de..1ce9df68cd 100644 --- a/src/status_im/ui/screens/signing/sheets.cljs +++ b/src/status_im/ui/screens/signing/sheets.cljs @@ -9,7 +9,7 @@ [status-im.signing.gas :as gas] [status-im.ui.components.icons.icons :as icons] [status-im.ui.components.react :as react] - [status-im.utils.money :as money])) + [utils.money :as money])) (views/defview fee-bottom-sheet [fee-display-symbol] diff --git a/src/status_im/ui/screens/stickers/views.cljs b/src/status_im/ui/screens/stickers/views.cljs index 5ac9986ac5..efe47b9cad 100644 --- a/src/status_im/ui/screens/stickers/views.cljs +++ b/src/status_im/ui/screens/stickers/views.cljs @@ -7,7 +7,7 @@ [status-im.ui.components.react :as react] [status-im.ui.screens.stickers.styles :as styles] [utils.re-frame :as rf] - [status-im.utils.money :as money]) + [utils.money :as money]) (:require-macros [status-im.utils.views :refer [defview letsubs]])) (defn cache diff --git a/src/status_im/ui/screens/wallet/send/views.cljs b/src/status_im/ui/screens/wallet/send/views.cljs index c5aad6de4c..a156c40edd 100644 --- a/src/status_im/ui/screens/wallet/send/views.cljs +++ b/src/status_im/ui/screens/wallet/send/views.cljs @@ -18,7 +18,7 @@ [status-im.ui.screens.wallet.components.views :as components] [status-im.ui.screens.wallet.send.sheets :as sheets] [status-im.ui.screens.wallet.send.styles :as styles] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.utils.utils :as utils] [status-im.wallet.utils :as wallet.utils])) diff --git a/src/status_im/utils/money.cljs b/src/status_im/utils/money.cljs deleted file mode 100644 index 63d75f34f7..0000000000 --- a/src/status_im/utils/money.cljs +++ /dev/null @@ -1,223 +0,0 @@ -(ns status-im.utils.money - (:require ["bignumber.js" :as BigNumber] - [clojure.string :as string])) - -;; The BigNumber version included in web3 sometimes hangs when dividing large -;; numbers Hence we want to use these functions instead of fromWei etc, which -;; come bundled with web3. See -;; https://github.com/MikeMcl/bignumber.js/issues/120 for this regression being -;; introduced in some JS environments. It is fixed in the MikeMcl/bignumber.js -;; repo, but not in the web3 BigNumber fork: -;; https://github.com/ethereum/web3.js/issues/877 -;; -;; Additionally, while it is possible to use the BigNumber constructor without -;; stringifying the number, this only works up to some 15 significant digits: -;; https://github.com/MikeMcl/bignumber.js/issues/120 -;; -;; Lastly, notice the bad rounding for native Javascript numbers above 17 digits -;; that may result in errors earlier up the call chain. Ideally all money-related -;; sensitive functions should be moved into this namespace to check for such -;; matters: -;; (str 111122223333441239) => "111122223333441230" - -(defn normalize - "A normalized string representation of an amount" - [s] - {:pre [(or (nil? s) (string? s))]} - (when s - (string/replace (string/trim s) #"," "."))) - -(defn bignumber - [n] - (when n - (try - (new BigNumber (normalize (str n))) - (catch :default _ nil)))) - -(defn greater-than-or-equals - [^js bn1 ^js bn2] - (.greaterThanOrEqualTo bn1 bn2)) - -(defn greater-than - [bn1 bn2] - (.greaterThan ^js bn1 bn2)) - -(defn equal-to - [bn1 bn2] - (.eq ^js bn1 bn2)) - -(defn sub - [bn1 bn2] - (.sub ^js bn1 bn2)) - -(defn valid? - [^js bn] - (when bn - (greater-than-or-equals bn 0))) - -(defn from-decimal - [n] - (when n - (str "1" (string/join (repeat n "0"))))) - -(def eth-units - {:wei (bignumber "1") - :kwei (bignumber (from-decimal 3)) - :mwei (bignumber (from-decimal 6)) - :gwei (bignumber (from-decimal 9)) - :szabo (bignumber (from-decimal 12)) - :finney (bignumber (from-decimal 15)) - :eth (bignumber (from-decimal 18)) - :keth (bignumber (from-decimal 21)) - :meth (bignumber (from-decimal 24)) - :geth (bignumber (from-decimal 27)) - :teth (bignumber (from-decimal 30))}) - -(defn wei-> - [unit n] - (when-let [^js bn (bignumber n)] - (.dividedBy bn (eth-units unit)))) - -(defn ->wei - [unit n] - (when-let [^js bn (bignumber n)] - (.times bn (eth-units unit)))) - -(defn to-fixed - ([^js bn] - (when bn - (.toFixed bn))) - ([^js bn b] - (when bn - (.toFixed bn b)))) - -(defn to-number - [^js bn] - (when bn - (.toNumber bn))) - -(defn to-string - ([^js bn] - (to-string bn 10)) - ([^js bn base] - (when bn - (.toString bn base)))) - -(defn to-hex - [^js bn] - (str "0x" (to-string bn 16))) - -(defn wei->str - ([unit n display-unit] - (str (to-fixed (wei-> unit n)) " " display-unit)) - ([unit n] (wei->str unit n (string/upper-case (name unit))))) - -(defn wei->ether - [n] - (wei-> :eth n)) - -(defn wei->gwei - [n] - (wei-> :gwei n)) - -(defn ether->wei - [^js bn] - (when bn - (.times bn ^js (bignumber 1e18)))) - -(defn token->unit - [n decimals] - (when-let [^js bn (bignumber n)] - (when-let [d (from-decimal decimals)] - (.dividedBy bn ^js (bignumber d))))) - -(defn unit->token - [n decimals] - (when-let [^js bn (bignumber n)] - (when-let [d (from-decimal decimals)] - (.times bn ^js (bignumber d))))) - -;;NOTE(goranjovic) - We have two basic representations of values that refer to cryptocurrency amounts: -;;formatted and -;; internal. Formatted representation is the one we show on screens and include in reports, whereas -;; internal -;; representation is the one that we pass on to ethereum network for execution, transfer, etc. -;; The difference between the two depends on the number of decimals, i.e. internal representation is -;; expressed in terms -;; of a whole number of smallest divisible parts of the formatted value. -;; -;; 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 -;; decimals parameter -;; to get the amount scale right. - -(defn formatted->internal - [n symbol decimals] - (if (= :ETH symbol) - (ether->wei n) - (unit->token n decimals))) - -(defn internal->formatted - [n symbol decimals] - (if (= :ETH symbol) - (wei->ether n) - (token->unit n decimals))) - -(defn fee-value - [gas gas-price] - (.times ^js (bignumber gas) ^js (bignumber gas-price))) - -(defn crypto->fiat - [crypto fiat-price] - (when-let [^js bn (bignumber crypto)] - (.times bn ^js (bignumber fiat-price)))) - -(defn percent-change - [from to] - (let [^js bnf (bignumber from) - ^js bnt (bignumber to)] - (when (and bnf bnt) - (-> ^js (.dividedBy bnf bnt) - ^js (.minus 1) - ^js (.times 100))))) - -(defn with-precision - [n decimals] - (when-let [^js bn (bignumber n)] - (.round bn decimals))) - -(defn sufficient-funds? - [^js amount ^js balance] - (when (and amount balance) - (.greaterThanOrEqualTo balance amount))) - -(defn fiat-amount-value - [amount-str from to prices] - (-> amount-str - (js/parseFloat) - bignumber - (crypto->fiat (get-in prices [from to] ^js (bignumber 0))) - (with-precision 2) - str)) - -(defn add - [bn1 n2] - (.add ^js bn1 n2)) - -(defn mul - [bn1 bn2] - (.mul ^js bn1 bn2)) - -(defn mul-and-round - [bn1 bn2] - (.round (.mul ^js bn1 bn2) 0)) - -(defn div - [bn1 bn2] - (.dividedBy ^js bn1 bn2)) - -(defn div-and-round - [bn1 bn2] - (.round (.dividedBy ^js bn1 bn2) 0)) diff --git a/src/status_im/utils/money_test.cljs b/src/status_im/utils/money_test.cljs deleted file mode 100644 index 6d276f2e61..0000000000 --- a/src/status_im/utils/money_test.cljs +++ /dev/null @@ -1,23 +0,0 @@ -(ns status-im.utils.money-test - (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.utils.money :as money])) - -(deftest wei->ether - (testing "Numeric input, 15 significant digits" - (is (= (str (money/wei->ether 111122223333444000)) - "0.111122223333444"))) - (testing "String input, 18 significant digits" - (is (= (str (money/wei->ether "111122223333441239")) - "0.111122223333441239")))) - -(deftest valid? - (is (not (true? (money/valid? nil)))) - (is (true? (money/valid? (money/bignumber 0)))) - (is (true? (money/valid? (money/bignumber 1)))) - (is (not (true? (money/valid? (money/bignumber -1)))))) - -(deftest normalize - (is (= nil (money/normalize nil))) - (is (= "1" (money/normalize " 1 "))) - (is (= "1.1" (money/normalize "1.1"))) - (is (= "1.1" (money/normalize "1,1")))) diff --git a/src/status_im/wallet/choose_recipient/core.cljs b/src/status_im/wallet/choose_recipient/core.cljs index 59bb2e7f48..d0bbba9854 100644 --- a/src/status_im/wallet/choose_recipient/core.cljs +++ b/src/status_im/wallet/choose_recipient/core.cljs @@ -10,7 +10,7 @@ [status-im.router.core :as router] [utils.re-frame :as rf] [status-im.utils.http :as http] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.utils.universal-links.utils :as links] [status-im.utils.wallet-connect :as wallet-connect] [status-im2.navigation.events :as navigation])) @@ -118,8 +118,7 @@ {:events [:wallet/parse-eip681-uri-and-resolve-ens]} [{db :db :as cofx} {:keys [message uri paths ens-names error]} ignore-url] (if-not error - ;; first we get a vector of ens-names to resolve and a vector of paths of - ;; these names + ;; first we get a vector of ens-names to resolve and a vector of paths of these names (if (empty? ens-names) ;; if there are no ens-names, we dispatch request-uri-parsed immediately (request-uri-parsed cofx message uri) @@ -130,8 +129,7 @@ (fn [addresses] (re-frame/dispatch [:wallet/request-uri-parsed - ;; we replace ens-names at their path in the message by their - ;; actual address + ;; we replace ens-names at their path in the message by their actual address (reduce (fn [message [path address]] (assoc-in message path address)) message diff --git a/src/status_im/wallet/core.cljs b/src/status_im/wallet/core.cljs index c7b9232e4a..707d34c1bf 100644 --- a/src/status_im/wallet/core.cljs +++ b/src/status_im/wallet/core.cljs @@ -21,7 +21,7 @@ [status-im.utils.core :as utils.core] [utils.re-frame :as rf] [utils.datetime :as datetime] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.utils.utils :as utils.utils] [status-im.wallet.db :as wallet.db] [status-im.wallet.prices :as prices] @@ -247,8 +247,8 @@ assets (get visible-tokens chain) tokens (->> (vals all-tokens) (remove #(or (:hidden? %) - ;;if not scan-all-tokens? remove not visible - ;;tokens + ;;if not scan-all-tokens? remove not + ;;visible tokens (and (not scan-all-tokens?) (not (get assets (:symbol %)))))) (reduce (fn [acc {:keys [address symbol]}] @@ -1141,8 +1141,7 @@ eip55/address->checksum) type (update :type keyword))] - ;; if the account is the default wallet we - ;; put it first in the list + ;; if the account is the default wallet we put it first in the list (if wallet (into [account] acc) (conj acc account))))) diff --git a/src/status_im/wallet/db.cljs b/src/status_im/wallet/db.cljs index 113f837877..e5be695f25 100644 --- a/src/status_im/wallet/db.cljs +++ b/src/status_im/wallet/db.cljs @@ -1,6 +1,6 @@ (ns status-im.wallet.db (:require [utils.i18n :as i18n] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.utils.priority-map :refer [empty-transaction-map]])) (defn- too-precise-amount? diff --git a/src/status_im/wallet/db_test.cljs b/src/status_im/wallet/db_test.cljs index 7afc497e6d..ad7c3e27db 100644 --- a/src/status_im/wallet/db_test.cljs +++ b/src/status_im/wallet/db_test.cljs @@ -1,7 +1,7 @@ (ns status-im.wallet.db-test (:require [cljs.test :refer-macros [deftest is testing]] [utils.i18n :as i18n] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.wallet.db :as wallet.db])) (deftest test-too-precise-amount? diff --git a/src/status_im/wallet/utils.cljs b/src/status_im/wallet/utils.cljs index 206e86bdbd..4707e60df1 100644 --- a/src/status_im/wallet/utils.cljs +++ b/src/status_im/wallet/utils.cljs @@ -1,5 +1,5 @@ (ns status-im.wallet.utils - (:require [status-im.utils.money :as money])) + (:require [utils.money :as money])) (defn format-amount [amount decimals] diff --git a/src/status_im2/subs/ens.cljs b/src/status_im2/subs/ens.cljs index 1c567b2dba..d853bae035 100644 --- a/src/status_im2/subs/ens.cljs +++ b/src/status_im2/subs/ens.cljs @@ -3,7 +3,7 @@ [re-frame.core :as re-frame] [status-im.ens.core :as ens] [status-im.ethereum.core :as ethereum] - [status-im.utils.money :as money])) + [utils.money :as money])) (re-frame/reg-sub :multiaccount/usernames diff --git a/src/status_im2/subs/wallet/signing.cljs b/src/status_im2/subs/wallet/signing.cljs index 70552210ed..a6b7818219 100644 --- a/src/status_im2/subs/wallet/signing.cljs +++ b/src/status_im2/subs/wallet/signing.cljs @@ -5,7 +5,7 @@ [status-im.ethereum.tokens :as tokens] [utils.i18n :as i18n] [status-im.signing.gas :as signing.gas] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.wallet.db :as wallet.db])) (re-frame/reg-sub diff --git a/src/status_im2/subs/wallet/transactions.cljs b/src/status_im2/subs/wallet/transactions.cljs index d9b70fd723..6729497483 100644 --- a/src/status_im2/subs/wallet/transactions.cljs +++ b/src/status_im2/subs/wallet/transactions.cljs @@ -4,7 +4,7 @@ [utils.i18n :as i18n] [status-im.notifications.core :as notifications] [utils.datetime :as datetime] - [status-im.utils.money :as money] + [utils.money :as money] [status-im.wallet.db :as wallet.db] [status-im.wallet.utils :as wallet.utils])) @@ -69,11 +69,12 @@ (reduce (fn [acc [hash transaction]] (assoc acc hash - (enrich-transaction transaction contacts native-currency))) ;;TODO this doesn't look - ;;good for performance, - ;;we need to calculate - ;;this only once for each - ;;transaction + (enrich-transaction transaction contacts native-currency))) ;;TODO this doesn't + ;;look good for + ;;performance, we + ;;need to calculate + ;;this only once for + ;;each transaction {} transactions))) diff --git a/src/status_im2/subs/wallet/wallet.cljs b/src/status_im2/subs/wallet/wallet.cljs index 8ad0de1e5d..2897e52792 100644 --- a/src/status_im2/subs/wallet/wallet.cljs +++ b/src/status_im2/subs/wallet/wallet.cljs @@ -4,7 +4,7 @@ [status-im.ethereum.core :as ethereum] [status-im.ethereum.tokens :as tokens] [status-im.utils.currency :as currency] - [status-im.utils.money :as money] + [utils.money :as money] [status-im2.config :as config] [utils.i18n :as i18n])) diff --git a/src/status_im2/subs/wallet/wallet_test.cljs b/src/status_im2/subs/wallet/wallet_test.cljs index 38af5266b8..67b6368ef8 100644 --- a/src/status_im2/subs/wallet/wallet_test.cljs +++ b/src/status_im2/subs/wallet/wallet_test.cljs @@ -2,7 +2,7 @@ (:require [cljs.test :refer [deftest is testing]] [re-frame.db :as rf-db] [test-helpers.unit :as h] - [status-im.utils.money :as money] + [utils.money :as money] [status-im2.subs.wallet.wallet :as wallet] [utils.re-frame :as rf])) diff --git a/src/utils/money.cljs b/src/utils/money.cljs index a7d671cf80..cbd1e4f98b 100644 --- a/src/utils/money.cljs +++ b/src/utils/money.cljs @@ -35,11 +35,194 @@ (new BigNumber (normalize (str n))) (catch :default _ nil)))) +(defn greater-than-or-equals + [^js bn1 ^js bn2] + (.greaterThanOrEqualTo bn1 bn2)) + +(defn greater-than + [bn1 bn2] + (.greaterThan ^js bn1 bn2)) + +(defn equal-to + [bn1 bn2] + (.eq ^js bn1 bn2)) + +(defn sub + [bn1 bn2] + (.sub ^js bn1 bn2)) + +(defn valid? + [^js bn] + (when bn + (greater-than-or-equals bn 0))) + +(defn from-decimal + [n] + (when n + (str "1" (string/join (repeat n "0"))))) + +(def eth-units + {:wei (bignumber "1") + :kwei (bignumber (from-decimal 3)) + :mwei (bignumber (from-decimal 6)) + :gwei (bignumber (from-decimal 9)) + :szabo (bignumber (from-decimal 12)) + :finney (bignumber (from-decimal 15)) + :eth (bignumber (from-decimal 18)) + :keth (bignumber (from-decimal 21)) + :meth (bignumber (from-decimal 24)) + :geth (bignumber (from-decimal 27)) + :teth (bignumber (from-decimal 30))}) + +(defn wei-> + [unit n] + (when-let [^js bn (bignumber n)] + (.dividedBy bn (eth-units unit)))) + +(defn ->wei + [unit n] + (when-let [^js bn (bignumber n)] + (.times bn (eth-units unit)))) + +(defn to-fixed + ([^js bn] + (when bn + (.toFixed bn))) + ([^js bn b] + (when bn + (.toFixed bn b)))) + +(defn to-number + [^js bn] + (when bn + (.toNumber bn))) + +(defn to-string + ([^js bn] + (to-string bn 10)) + ([^js bn base] + (when bn + (.toString bn base)))) + +(defn to-hex + [^js bn] + (str "0x" (to-string bn 16))) + +(defn wei->str + ([unit n display-unit] + (str (to-fixed (wei-> unit n)) " " display-unit)) + ([unit n] (wei->str unit n (string/upper-case (name unit))))) + +(defn wei->ether + [n] + (wei-> :eth n)) + +(defn wei->gwei + [n] + (wei-> :gwei n)) + +(defn ether->wei + [^js bn] + (when bn + (.times bn ^js (bignumber 1e18)))) + +(defn token->unit + [n decimals] + (when-let [^js bn (bignumber n)] + (when-let [d (from-decimal decimals)] + (.dividedBy bn ^js (bignumber d))))) + +(defn unit->token + [n decimals] + (when-let [^js bn (bignumber n)] + (when-let [d (from-decimal decimals)] + (.times bn ^js (bignumber d))))) + +;;NOTE(goranjovic) - We have two basic representations of values that refer to cryptocurrency amounts: +;;formatted and +;; internal. Formatted representation is the one we show on screens and include in reports, whereas +;; internal +;; representation is the one that we pass on to ethereum network for execution, transfer, etc. +;; The difference between the two depends on the number of decimals, i.e. internal representation is +;; expressed in terms +;; of a whole number of smallest divisible parts of the formatted value. +;; +;; 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 +;; decimals parameter +;; to get the amount scale right. + +(defn formatted->internal + [n symbol decimals] + (if (= :ETH symbol) + (ether->wei n) + (unit->token n decimals))) + +(defn internal->formatted + [n symbol decimals] + (if (= :ETH symbol) + (wei->ether n) + (token->unit n decimals))) + +(defn fee-value + [gas gas-price] + (.times ^js (bignumber gas) ^js (bignumber gas-price))) + +(defn crypto->fiat + [crypto fiat-price] + (when-let [^js bn (bignumber crypto)] + (.times bn ^js (bignumber fiat-price)))) + +(defn percent-change + [from to] + (let [^js bnf (bignumber from) + ^js bnt (bignumber to)] + (when (and bnf bnt) + (-> ^js (.dividedBy bnf bnt) + ^js (.minus 1) + ^js (.times 100))))) + (defn with-precision [n decimals] (when-let [^js bn (bignumber n)] (.round bn decimals))) +(defn sufficient-funds? + [^js amount ^js balance] + (when (and amount balance) + (.greaterThanOrEqualTo balance amount))) + +(defn fiat-amount-value + [amount-str from to prices] + (-> amount-str + (js/parseFloat) + bignumber + (crypto->fiat (get-in prices [from to] ^js (bignumber 0))) + (with-precision 2) + str)) + +(defn add + [bn1 n2] + (.add ^js bn1 n2)) + +(defn mul + [bn1 bn2] + (.mul ^js bn1 bn2)) + +(defn mul-and-round + [bn1 bn2] + (.round (.mul ^js bn1 bn2) 0)) + +(defn div + [bn1 bn2] + (.dividedBy ^js bn1 bn2)) + +(defn div-and-round + [bn1 bn2] + (.round (.dividedBy ^js bn1 bn2) 0)) + (defn format-amount "Format `amount` to thousands or millions. Return nil if `amount` is not truthy." [amount] diff --git a/src/utils/money_test.cljs b/src/utils/money_test.cljs index a5820f6bfb..f2999bde5b 100644 --- a/src/utils/money_test.cljs +++ b/src/utils/money_test.cljs @@ -1,7 +1,21 @@ (ns utils.money-test - (:require [cljs.test :refer-macros [deftest is are]] + (:require [cljs.test :refer-macros [deftest testing is are]] [utils.money :as money])) +(deftest wei->ether + (testing "Numeric input, 15 significant digits" + (is (= (str (money/wei->ether 111122223333444000)) + "0.111122223333444"))) + (testing "String input, 18 significant digits" + (is (= (str (money/wei->ether "111122223333441239")) + "0.111122223333441239")))) + +(deftest valid? + (is (not (true? (money/valid? nil)))) + (is (true? (money/valid? (money/bignumber 0)))) + (is (true? (money/valid? (money/bignumber 1)))) + (is (not (true? (money/valid? (money/bignumber -1)))))) + (deftest normalize (is (= nil (money/normalize nil))) (is (= "1" (money/normalize " 1 ")))