Skip to content

Commit cc6dcc3

Browse files
authored
Fix rounding of fiat and crypto on send page (#20915)
* Fix conversion rounding on send page * fixing review notes * fix amount of allowed input decimals for crypto
1 parent 9d6b860 commit cc6dcc3

File tree

9 files changed

+182
-137
lines changed

9 files changed

+182
-137
lines changed

src/quo/components/wallet/token_input/component_spec.cljs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
(h/describe "Wallet: Token Input"
77
(h/test "Token label renders"
88
(h/render-with-theme-provider [token-input/view
9-
{:token :snt
10-
:currency :eur
11-
:currency-symbol ""
12-
:conversion 1}])
9+
{:token :snt
10+
:currency :eur
11+
:crypto? true
12+
:conversion 1}])
1313
(h/is-truthy (h/get-by-text "SNT")))
1414

1515
(h/test "Amount renders"
1616
(h/render-with-theme-provider [token-input/view
1717
{:token :snt
1818
:currency :eur
19-
:currency-symbol ""
19+
:converted-value "0.00"
2020
:conversion 1}])
2121
(h/is-truthy (h/get-by-text "€0.00"))))

src/quo/components/wallet/token_input/view.cljs

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,10 @@
1111
[quo.components.wallet.token-input.style :as style]
1212
[quo.theme :as quo.theme]
1313
[react-native.core :as rn]
14-
[schema.core :as schema]
15-
[utils.number :as number]))
16-
17-
(defn fiat-format
18-
[currency-symbol num-value conversion]
19-
(str currency-symbol (.toFixed (* num-value conversion) 2)))
20-
21-
(defn crypto-format
22-
[num-value conversion crypto-decimals token]
23-
(str (number/remove-trailing-zeroes
24-
(.toFixed (/ num-value conversion) (or crypto-decimals 2)))
25-
" "
26-
(string/upper-case (or (clj->js token) ""))))
27-
28-
(defn calc-value
29-
[{:keys [crypto? currency-symbol token value conversion crypto-decimals]}]
30-
(let [num-value (if (string? value)
31-
(or (parse-double value) 0)
32-
value)]
33-
(if crypto?
34-
(fiat-format currency-symbol num-value conversion)
35-
(crypto-format num-value conversion crypto-decimals token))))
14+
[schema.core :as schema]))
3615

3716
(defn- data-info
38-
[{:keys [theme token crypto-decimals conversion networks title crypto? currency-symbol amount
39-
error?]}]
17+
[{:keys [theme networks title converted-value error?]}]
4018
[rn/view {:style style/data-container}
4119
[network-tag/view
4220
{:networks networks
@@ -46,12 +24,7 @@
4624
{:size :paragraph-2
4725
:weight :medium
4826
:style (style/fiat-amount theme)}
49-
(calc-value {:crypto? crypto?
50-
:currency-symbol currency-symbol
51-
:token token
52-
:value amount
53-
:conversion conversion
54-
:crypto-decimals crypto-decimals})]])
27+
converted-value]])
5528

5629
(defn- token-name-text
5730
[theme text]
@@ -123,16 +96,14 @@
12396
:i/reorder]]))))
12497

12598
(defn- view-internal
126-
[{:keys [container-style value on-swap] :as props}]
99+
[{:keys [container-style on-swap crypto?] :as props}]
127100
(let [theme (quo.theme/use-theme)
128101
width (:width (rn/get-window))
129102
[value-internal set-value-internal] (rn/use-state nil)
130-
[crypto? set-crypto] (rn/use-state true)
131103
handle-on-swap (rn/use-callback
132104
(fn []
133-
(set-crypto (not crypto?))
134105
(when on-swap (on-swap (not crypto?))))
135-
[crypto? on-swap])]
106+
[on-swap])]
136107
[rn/view {:style (merge (style/main-container width) container-style)}
137108
[rn/view {:style style/amount-container}
138109
[input-section
@@ -143,10 +114,6 @@
143114
:handle-on-swap handle-on-swap
144115
:crypto? crypto?)]]
145116
[divider-line/view {:container-style (style/divider theme)}]
146-
[data-info
147-
(assoc props
148-
:theme theme
149-
:crypto? crypto?
150-
:amount (or value value-internal))]]))
117+
[data-info (assoc props :theme theme)]]))
151118

152119
(def view (schema/instrument #'view-internal component-schema/?schema))

src/status_im/contexts/preview/quo/wallet/token_input.cljs

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
(:require
33
[quo.core :as quo]
44
[quo.foundations.resources :as resources]
5+
[react-native.core :as rn]
56
[react-native.safe-area :as safe-area]
67
[reagent.core :as reagent]
7-
[status-im.contexts.preview.quo.preview :as preview]))
8+
[status-im.common.controlled-input.utils :as controlled-input]
9+
[status-im.contexts.preview.quo.preview :as preview]
10+
[status-im.contexts.wallet.common.utils :as utils]
11+
[utils.money :as money]
12+
[utils.number :as number]))
813

914
(def networks
1015
[{:source (resources/get-network :arbitrum)}
@@ -20,39 +25,72 @@
2025
{:key :snt}]}
2126
{:key :currency
2227
:type :select
23-
:options [{:key :usd}
24-
{:key :eur}]}
28+
:options [{:key "$"}
29+
{:key ""}]}
2530
{:key :error?
2631
:type :boolean}
2732
{:key :allow-selection?
2833
:type :boolean}])
2934

3035
(defn view
3136
[]
32-
(let [state (reagent/atom {:token :eth
33-
:currency :usd
34-
:conversion 0.02
35-
:networks networks
36-
:title title
37-
:customization-color :blue
38-
:show-keyboard? false
39-
:allow-selection? true})
40-
value (reagent/atom "")
41-
set-value (fn [v]
42-
(swap! value str v))
43-
delete (fn [_]
44-
(swap! value #(subs % 0 (dec (count %)))))]
37+
(let [state (reagent/atom {:token :eth
38+
:currency "$"
39+
:conversion-rate 3450.28
40+
:networks networks
41+
:title title
42+
:customization-color :blue
43+
:show-keyboard? false
44+
:allow-selection? true
45+
:crypto? true})]
4546
(fn []
46-
[preview/preview-container
47-
{:state state
48-
:descriptor descriptor
49-
:full-screen? true
50-
:component-container-style {:flex 1
51-
:justify-content :space-between}}
52-
[quo/token-input (assoc @state :value @value)]
53-
[quo/numbered-keyboard
54-
{:container-style {:padding-bottom (safe-area/get-top)}
55-
:left-action :dot
56-
:delete-key? true
57-
:on-press set-value
58-
:on-delete delete}]])))
47+
(let [{:keys [currency token conversion-rate
48+
crypto?]} @state
49+
[input-state set-input-state] (rn/use-state controlled-input/init-state)
50+
input-amount (controlled-input/input-value input-state)
51+
swap-between-fiat-and-crypto (fn []
52+
(set-input-state
53+
(fn [input-state]
54+
(controlled-input/set-input-value
55+
input-state
56+
(let [new-value
57+
(if-not crypto?
58+
(utils/cut-crypto-decimals-to-fit-usd-cents
59+
conversion-rate
60+
(money/fiat->crypto input-amount
61+
conversion-rate))
62+
(utils/cut-fiat-balance-to-two-decimals
63+
(money/crypto->fiat input-amount
64+
conversion-rate)))]
65+
(number/remove-trailing-zeroes
66+
new-value))))))
67+
converted-value (if crypto?
68+
(utils/prettify-balance currency
69+
(money/crypto->fiat input-amount
70+
conversion-rate))
71+
(utils/prettify-crypto-balance
72+
(or (clj->js token) "")
73+
(money/fiat->crypto input-amount conversion-rate)
74+
conversion-rate))]
75+
[preview/preview-container
76+
{:state state
77+
:descriptor descriptor
78+
:full-screen? true
79+
:component-container-style {:flex 1
80+
:justify-content :space-between}}
81+
[quo/token-input
82+
(merge @state
83+
{:value input-amount
84+
:converted-value converted-value
85+
:on-swap (fn [crypto]
86+
(swap! state assoc :crypto? crypto)
87+
(swap-between-fiat-and-crypto))})]
88+
[quo/numbered-keyboard
89+
{:container-style {:padding-bottom (safe-area/get-top)}
90+
:left-action :dot
91+
:delete-key? true
92+
:on-press (fn [c]
93+
(set-input-state #(controlled-input/add-character % c)))
94+
95+
:on-delete (fn []
96+
(set-input-state controlled-input/delete-last))}]]))))

src/status_im/contexts/wallet/common/utils.cljs

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@
1212
[full-name]
1313
(first (string/split full-name #" ")))
1414

15-
(defn prettify-balance
16-
[currency-symbol balance]
15+
(defn cut-fiat-balance-to-two-decimals
16+
[balance]
1717
(let [valid-balance? (and balance
1818
(or (number? balance) (.-toFixed balance)))]
1919
(as-> balance $
2020
(if valid-balance? $ 0)
21-
(.toFixed $ 2)
22-
(str currency-symbol $))))
21+
(.toFixed $ 2))))
22+
23+
(defn prettify-balance
24+
[currency-symbol balance]
25+
(str currency-symbol (cut-fiat-balance-to-two-decimals balance)))
2326

2427
(defn get-derivation-path
2528
[number-of-accounts]
@@ -47,8 +50,8 @@
4750
nil))
4851

4952
(defn calc-max-crypto-decimals
50-
[value]
51-
(let [str-representation (str value)
53+
[one-cent-value]
54+
(let [str-representation (str one-cent-value)
5255
decimal-part (second (clojure.string/split str-representation #"\."))
5356
exponent (extract-exponent str-representation)
5457
zeroes-count (count (take-while #(= \0 %) decimal-part))
@@ -58,29 +61,60 @@
5861
(inc max-decimals)
5962
max-decimals)))
6063

61-
(defn get-crypto-decimals-count
62-
[{:keys [market-values-per-currency]}]
63-
(let [price (get-in market-values-per-currency [:usd :price])
64-
one-cent-value (if (pos? price) (/ 0.01 price) 0)]
65-
(calc-max-crypto-decimals one-cent-value)))
64+
(defn token-usd-price
65+
[token]
66+
(get-in token [:market-values-per-currency :usd :price]))
67+
68+
(defn one-cent-value
69+
[token-price-in-usd]
70+
(if (pos? token-price-in-usd)
71+
(/ 0.01 token-price-in-usd)
72+
0))
73+
74+
(defn analyze-token-amount-for-price
75+
"For full details: https://github.com/status-im/status-mobile/issues/18225"
76+
[token-price-in-usd token-units]
77+
(if (or (nil? token-units)
78+
(not (money/bignumber? token-units))
79+
(money/equal-to token-units 0))
80+
{:zero-value? true}
81+
(let [cent-value (one-cent-value token-price-in-usd)]
82+
{:usd-cent-value cent-value
83+
:standardized-decimals-count (if (nil? token-price-in-usd)
84+
missing-price-decimals
85+
(calc-max-crypto-decimals cent-value))})))
86+
87+
(defn cut-crypto-decimals-to-fit-usd-cents
88+
[token-price-in-usd token-units]
89+
(let [{:keys [zero-value? usd-cent-value standardized-decimals-count]}
90+
(analyze-token-amount-for-price token-price-in-usd token-units)]
91+
(cond
92+
zero-value? "0"
93+
(< token-units usd-cent-value) "0"
94+
:else (number/remove-trailing-zeroes
95+
(.toFixed token-units standardized-decimals-count)))))
96+
97+
(defn prettify-crypto-balance
98+
[token-symbol crypto-balance conversion-rate]
99+
(str (cut-crypto-decimals-to-fit-usd-cents conversion-rate crypto-balance)
100+
" "
101+
(string/upper-case token-symbol)))
66102

67103
(defn get-standard-crypto-format
68104
"For full details: https://github.com/status-im/status-mobile/issues/18225"
69-
[{:keys [market-values-per-currency]} token-units]
70-
(cond (or (nil? token-units)
71-
(money/equal-to token-units 0))
72-
"0"
73-
74-
(nil? (-> market-values-per-currency :usd :price))
75-
(number/remove-trailing-zeroes (.toFixed token-units missing-price-decimals))
76-
77-
:else
78-
(let [price (-> market-values-per-currency :usd :price)
79-
one-cent-value (if (pos? price) (/ 0.01 price) 0)
80-
decimals-count (calc-max-crypto-decimals one-cent-value)]
81-
(if (< token-units one-cent-value)
82-
(str "<" (number/remove-trailing-zeroes (.toFixed one-cent-value decimals-count)))
83-
(number/remove-trailing-zeroes (.toFixed token-units decimals-count))))))
105+
[token token-units]
106+
(let [token-price-in-usd (token-usd-price token)
107+
{:keys [zero-value? usd-cent-value standardized-decimals-count]}
108+
(analyze-token-amount-for-price token-price-in-usd token-units)]
109+
(cond
110+
zero-value?
111+
"0"
112+
113+
(< token-units usd-cent-value)
114+
(str "<" (number/remove-trailing-zeroes (.toFixed usd-cent-value standardized-decimals-count)))
115+
116+
:else
117+
(number/remove-trailing-zeroes (.toFixed token-units standardized-decimals-count)))))
84118

85119
(defn get-market-value
86120
[currency {:keys [market-values-per-currency]}]

src/status_im/contexts/wallet/common/utils_test.cljs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@
7373
token-units (money/bignumber 0.01)]
7474
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
7575
token-units)
76-
"<2")))))
76+
"<2")))
77+
(let [market-values-per-currency {:usd {:price 0.005}}
78+
token-units "0.01"]
79+
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
80+
token-units)
81+
"0")))))
7782

7883
(deftest calculate-total-token-balance-test
7984
(testing "calculate-total-token-balance function"
@@ -163,5 +168,3 @@
163168
expected-order ["DAI" "ETH" "SNT"]]
164169
(is (= expected-order sorted-tokens)))))
165170

166-
167-

src/status_im/contexts/wallet/send/input_amount/component_spec.cljs

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -113,34 +113,11 @@
113113
:limit-crypto 250
114114
:initial-crypto-currency? false}])
115115
(h/is-truthy (h/get-by-text "0"))
116-
(h/is-truthy (h/get-by-text "ETH"))
117-
(h/is-truthy (h/get-by-text "$0.00"))
116+
(h/is-truthy (h/get-by-text "USD"))
117+
(h/is-truthy (h/get-by-text "0 ETH"))
118118
(h/is-truthy (h/get-by-label-text :container))
119119
(h/is-disabled (h/get-by-label-text :button-one)))
120120

121-
(h/test "Fill token input and confirm"
122-
(h/setup-subs sub-mocks)
123-
(let [on-confirm (h/mock-fn)]
124-
(h/render-with-theme-provider [input-amount/view
125-
{:on-confirm on-confirm
126-
:crypto-decimals 10
127-
:limit-crypto 1000
128-
:initial-crypto-currency? false}])
129-
130-
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
131-
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
132-
(h/fire-event :press (h/query-by-label-text :keyboard-key-3))
133-
(h/fire-event :press (h/query-by-label-text :keyboard-key-.))
134-
(h/fire-event :press (h/query-by-label-text :keyboard-key-4))
135-
(h/fire-event :press (h/query-by-label-text :keyboard-key-5))
136-
137-
(-> (h/wait-for #(h/get-by-text "$1234.50"))
138-
(.then (fn []
139-
(h/is-truthy (h/get-by-label-text :button-one))
140-
(h/is-truthy (h/get-by-label-text :container))
141-
(h/fire-event :press (h/get-by-label-text :button-one))
142-
(h/was-called on-confirm))))))
143-
144121
(h/test "Fill token input and confirm"
145122
(h/setup-subs sub-mocks)
146123

@@ -149,7 +126,7 @@
149126
{:crypto-decimals 10
150127
:limit-crypto 1000
151128
:on-confirm on-confirm
152-
:initial-crypto-currency? false}])
129+
:initial-crypto-currency? true}])
153130

154131
(h/fire-event :press (h/query-by-label-text :keyboard-key-1))
155132
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))

0 commit comments

Comments
 (0)