Move status-im.utils.money to utils.money (#16573)

This commit is contained in:
Icaro Motta 2023-07-12 18:26:04 +00:00 committed by GitHub
parent 645a5b312b
commit 88c4521321
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 234 additions and 285 deletions

View File

@ -1,5 +1,5 @@
(ns status-im.ethereum.decode
(:require [status-im.utils.money :as money]))
(:require [utils.money :as money]))
(defn uint
[hex]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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