Introduce a money namespace that solves a bunch of issues related to significant
numbers being preserved and converted correctly.
This commit is contained in:
Oskar Thorén 2017-08-03 16:51:36 +02:00 committed by Roman Volosovskyi
parent 6b0f09faf8
commit b510488f11
11 changed files with 88 additions and 25 deletions

View File

@ -41,7 +41,8 @@
"nfc-react-native",
"react-native-http-bridge",
"emojilib",
"react-native-mapbox-gl"
"react-native-mapbox-gl",
"bignumber.js"
],
"imageDirs": [
"images"

View File

@ -9,8 +9,8 @@ var TopLevel = {
"alert" : function () {},
"Animated" : function () {},
"Array" : function () {},
"AutoGrowingTextInput" : function () {},
"awesome-phonenumber" : function () {},
"BigNumber" : function () {},
"blur" : function () {},
"call" : function () {},
"callJail" : function () {},
@ -23,6 +23,7 @@ var TopLevel = {
"clearInterval" : function () {},
"clearStorageAPIs" : function () {},
"clearTimeout" : function () {},
"clearWatch" : function () {},
"Clipboard" : function () {},
"cloneWithRows" : function () {},
"close" : function () {},
@ -50,12 +51,14 @@ var TopLevel = {
"digest" : function () {},
"Dimensions" : function () {},
"discardTransaction" : function () {},
"dividedBy" : function () {},
"dy" : function () {},
"encrypt" : function () {},
"ENC_DEC" : function () {},
"end" : function () {},
"endCoordinates" : function () {},
"Error" : function () {},
"ErrorUtils" : function () {},
"eth" : function () {},
"event" : function () {},
"fallbacks" : function () {},
@ -74,6 +77,7 @@ var TopLevel = {
"getAll" : function () {},
"getBlock" : function () {},
"getCardId" : function () {},
"getCurrentPosition" : function () {},
"getExample" : function () {},
"getInitialOrientation" : function () {},
"getLayout" : function () {},
@ -143,7 +147,7 @@ var TopLevel = {
"ReactNative" : function () {},
"readFile" : function () {},
"readTag" : function () {},
"realm-class" : function () {},
"realm" : function () {},
"recoverAccount" : function () {},
"registerComponent" : function () {},
"reload" : function () {},
@ -167,6 +171,8 @@ var TopLevel = {
"sendWeb3Request" : function () {},
"sequence" : function () {},
"set" : function () {},
"setAccessToken" : function () {},
"setGlobalHandler" : function () {},
"setInterval" : function () {},
"setNativeProps" : function () {},
"setSoftInputMode" : function () {},
@ -219,8 +225,8 @@ var TopLevel = {
"ValueXY" : function () {},
"View" : function () {},
"vy" : function () {},
"watchPosition" : function () {},
"Web3" : function () {},
"web3" : function () {},
"width" : function () {},
"window" : function () {},
"write" : function () {},

29
package-lock.json generated
View File

@ -4,11 +4,6 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"Base64": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
"integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
},
"abbrev": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
@ -968,6 +963,11 @@
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
"integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs="
},
"Base64": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
"integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
},
"base64-js": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
@ -1008,7 +1008,9 @@
"integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak="
},
"bignumber.js": {
"version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.0.2.tgz",
"integrity": "sha1-LR3DfuWWiGfs6pC22k0W5oYI0h0="
},
"bindings": {
"version": "1.3.0",
@ -5813,6 +5815,11 @@
}
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"string-range": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/string-range/-/string-range-1.2.2.tgz",
@ -5833,11 +5840,6 @@
"resolved": "https://registry.npmjs.org/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz",
"integrity": "sha1-jZeDM8C8klOPUPOD5IiPPlYZ1lM="
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
@ -6265,6 +6267,11 @@
"utf8": "2.1.2",
"xhr2": "0.1.4",
"xmlhttprequest": "1.8.0"
},
"dependencies": {
"bignumber.js": {
"version": "git+https://github.com/debris/bignumber.js.git#94d7146671b9719e00a09c29b01a691bc85048c2"
}
}
},
"whatwg-fetch": {

View File

@ -22,6 +22,7 @@
"babel-plugin-transform-regenerator": "6.20.0",
"babel-preset-react-native": "1.9.0",
"babel-register": "6.18.0",
"bignumber.js": "^4.0.2",
"browserify-zlib": "^0.1.4",
"buffer": "^3.6.0",
"chance": "1.0.4",

View File

@ -27,7 +27,8 @@
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.listview :as lw]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]))
[status-im.utils.utils :as utils]
[status-im.utils.money :as money]))
(defonce drawer-atom (atom nil))
(defn open-drawer [] (.openDrawer @drawer-atom))
@ -95,7 +96,7 @@
(defview transaction-list-item [{:keys [to value timestamp] :as transaction}]
[recipient [:contact-by-address to]]
(let [eth-value (.fromWei js/Web3.prototype value "ether")
(let [eth-value (str (money/wei->ether value))
value (i18n/label-number eth-value)
recipient-name (or (:name recipient) to)]
[touchable-highlight {:on-press #(rf/dispatch [:navigate-to-modal :transaction-details transaction])}

View File

@ -7,3 +7,4 @@
(def homoglyph-finder (js/require "homoglyph-finder"))
(def identicon-js (js/require "identicon.js"))
(def Web3 (js/require "web3"))
(def BigNumber (js/require "bignumber.js"))

View File

@ -12,7 +12,8 @@
[status-im.transactions.styles.screens :as st]
[status-im.transactions.views.list-item :as transactions-list-item]
[status-im.transactions.views.password-form :as password-form]
[status-im.utils.platform :as platform]))
[status-im.utils.platform :as platform]
[status-im.utils.money :as money]))
(defn toolbar-view []
[toolbar/toolbar
@ -40,8 +41,8 @@
[current-account [:get-current-account]
recipient [:contact-by-address to]]
(let [recipient-name (or (:name recipient) to (i18n/label :t/contract-creation))
gas-price (.fromWei js/Web3.prototype gas-price "ether")
fee-value (* gas gas-price)
gas-price' (money/wei->ether gas-price)
fee-value (money/fee-value gas gas-price')
estimated-fee (str fee-value " ETH")]
[rn/view st/details-container
[detail-item (i18n/label :t/to) recipient-name true]

View File

@ -4,7 +4,8 @@
[status-im.components.chat-icon.screen :as chat-icon]
[status-im.components.react :as rn]
[status-im.i18n :as i18n]
[status-im.transactions.styles.list-item :as st]))
[status-im.transactions.styles.list-item :as st]
[status-im.utils.money :as money]))
(defview item-image [contact]
[rn/view {:style st/item-photo}
@ -30,8 +31,7 @@
(defview view [{:keys [to value id] :as transaction} on-deny]
[recipient [:contact-by-address to]]
(let [bignumber (.toBigNumber js/Web3.prototype value)
eth-value (str (.fromWei js/Web3.prototype bignumber "ether"))
(let [eth-value (str (money/wei->ether value))
value-str (str (i18n/label-number eth-value) " ETH")
recipient-name (or (:name recipient) to (i18n/label :t/contract-creation))]
[rn/view {:style st/item}

View File

@ -0,0 +1,31 @@
(ns status-im.utils.money
(:require [status-im.js-dependencies :as dependencies]))
;; 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 bignumber [n]
(dependencies/BigNumber. (str n)))
(def ether-unit-value (bignumber "1000000000000000000"))
(defn wei->ether [n]
(.dividedBy (bignumber n) ether-unit-value))
(defn fee-value [gas gas-price]
(.times (bignumber gas) (bignumber gas-price)))

View File

@ -3,7 +3,8 @@
[status-im.test.contacts.handlers]
[status-im.test.chat.models.input]
[status-im.test.handlers]
[status-im.test.utils.utils]))
[status-im.test.utils.utils]
[status-im.test.utils.money]))
(enable-console-print!)
@ -16,4 +17,5 @@
(doo-tests 'status-im.test.contacts.handlers
'status-im.test.chat.models.input
'status-im.test.handlers
'status-im.test.utils.utils)
'status-im.test.utils.utils
'status-im.test.utils.money)

View File

@ -0,0 +1,12 @@
(ns status-im.test.utils.money
(: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"))))