bug #4123 - fixes incorrect number of decimals for tokens, fixes incorrect network id lookup for token send, fixes token amount formatting where needed

This commit is contained in:
Goran Jovic 2018-05-24 12:58:39 +02:00 committed by Roman Volosovskyi
parent ae56eec40f
commit 5e33c98008
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
11 changed files with 87 additions and 42 deletions

View File

@ -14,7 +14,7 @@
(defn send-shortcut-fx [db contact params]
(merge {:db (-> db
(send.events/set-and-validate-amount-db (:amount params))
(send.events/set-and-validate-amount-db (:amount params) :ETH 18)
(choose-recipient.events/fill-request-details (transaction-details contact))
(navigation/navigate-to :wallet-send-transaction-chat))}
(send.events/update-gas-price db false)))

View File

@ -86,3 +86,7 @@
(fn [db [_ item-id]]
(let [item-animation (get-in db [:chat-animations item-id])]
(if (some? item-animation) (:delete-swiped item-animation) nil))))
(reg-sub :get-current-account-network
(fn [{:keys [network] :as db} [_]]
(get-in db [:account/account :networks network])))

View File

@ -185,7 +185,8 @@
[recipient-contact address name request?]
[recipient-address address])]])
(defn- amount-input [{:keys [input-options amount amount-text disabled?]}]
(defn- amount-input [{:keys [input-options amount amount-text disabled?]}
{:keys [symbol decimals]}]
[react/view {:style components.styles/flex
:accessibility-label :specify-amount-button}
[components/text-input
@ -195,7 +196,9 @@
;; Otherwise, user might want to fix his input and autocorrection will give more harm than good.
;; Positive check is because we don't want to replace unfinished 0.000 with just plain 0, that is annoying and
;; potentially dangerous on this screen (e.g. sending 7 ETH instead of 0.0007)
{:default-value (if (empty? amount-text) (str (money/to-fixed (money/wei->ether amount))) amount-text)}
{:default-value (if (empty? amount-text)
(str (money/to-fixed (money/internal->formatted amount symbol decimals)))
amount-text)}
(if disabled?
{:editable false}
{:keyboard-type :numeric
@ -203,7 +206,7 @@
:style components.styles/flex
:accessibility-label :amount-input}))]])
(defn amount-selector [{:keys [error disabled?] :as m}]
(defn amount-selector [{:keys [error disabled?] :as m} token]
[react/view
[components/cartouche {:disabled? disabled?}
(i18n/label :t/amount)

View File

@ -16,11 +16,11 @@
;; Placeholder namespace for wallet specs, which are a WIP depending on data
;; model we decide on for balances, prices, etc.
(defn- too-precise-amount? [amount]
(defn- too-precise-amount? [amount decimals]
(let [amount-splited (string/split amount #"[.]")]
(and (= (count amount-splited) 2) (> (count (last amount-splited)) 18))))
(and (= (count amount-splited) 2) (> (count (last amount-splited)) decimals))))
(defn parse-amount [amount]
(defn parse-amount [amount decimals]
(when-not (empty? amount)
(let [normalized-amount (money/normalize amount)
value (money/bignumber normalized-amount)]
@ -28,7 +28,7 @@
(not (money/valid? value))
{:error (i18n/label :t/validation-amount-invalid-number) :value value}
(too-precise-amount? normalized-amount)
(too-precise-amount? normalized-amount decimals)
{:error (i18n/label :t/validation-amount-is-too-precise) :value value}
:else

View File

@ -35,7 +35,7 @@
(handlers/register-handler-fx
:wallet.request/set-and-validate-amount
(fn [{:keys [db]} [_ amount]]
(let [{:keys [value error]} (wallet-db/parse-amount amount)]
(let [{:keys [value error]} (wallet-db/parse-amount amount :ETH)]
{:db (-> db
(assoc-in [:wallet :request-transaction :amount] (money/ether->wei value))
(assoc-in [:wallet :request-transaction :amount-text] amount)

View File

@ -45,7 +45,9 @@
:amount-text amount-text
:input-options {:max-length 21
:on-focus (fn [] (when @scroll (utils/set-timeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount %])}}]]]
:on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount %])}}
{:decimals 18
:symbol :ETH}]]]
[bottom-buttons/bottom-buttons styles/bottom-buttons
nil ;; Force a phantom button to ensure consistency with other transaction screens which define 2 buttons
[button/button {:disabled? (not (and to amount))

View File

@ -29,7 +29,7 @@
#()))
(defn- send-tokens [{:keys [web3 from to value gas gas-price symbol network]}]
(let [contract (:address (tokens/symbol->token (ethereum/network->chain-keyword network) symbol))]
(let [contract (:address (tokens/symbol->token (keyword (ethereum/network-names network)) symbol))]
(erc20/transfer web3 contract from to value {:gas gas :gasPrice gas-price} #())))
(re-frame/reg-fx
@ -68,22 +68,25 @@
(re-frame/dispatch [::transaction-completed {:id (name (key result)) :response (second result)} modal?]))
;;;; Handlers
(defn set-and-validate-amount-db [db amount]
(let [{:keys [value error]} (wallet.db/parse-amount amount)]
(defn set-and-validate-amount-db [db amount symbol decimals]
(let [{:keys [value error]} (wallet.db/parse-amount amount decimals)]
(-> db
(assoc-in [:wallet :send-transaction :amount] (money/ether->wei value))
(assoc-in [:wallet :send-transaction :amount] (money/formatted->internal value symbol decimals))
(assoc-in [:wallet :send-transaction :amount-text] amount)
(assoc-in [:wallet :send-transaction :amount-error] error))))
(handlers/register-handler-fx
:wallet.send/set-and-validate-amount
(fn [{:keys [db]} [_ amount]]
{:db (set-and-validate-amount-db db amount)}))
(fn [{:keys [db]} [_ amount symbol decimals]]
{:db (set-and-validate-amount-db db amount symbol decimals)}))
(handlers/register-handler-fx
:wallet.send/set-symbol
(fn [{:keys [db]} [_ symbol]]
{:db (-> (assoc-in db [:wallet :send-transaction :symbol] symbol)
{:db (-> db
(assoc-in [:wallet :send-transaction :symbol] symbol)
(assoc-in [:wallet :send-transaction :amount] nil)
(assoc-in [:wallet :send-transaction :amount-text] nil)
(assoc-in [:wallet :send-transaction :gas] (ethereum/estimate-gas symbol)))}))
(handlers/register-handler-fx

View File

@ -21,7 +21,9 @@
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.utils.money :as money]
[status-im.utils.utils :as utils]
[status-im.transport.utils :as transport.utils]))
[status-im.transport.utils :as transport.utils]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.ethereum.core :as ethereum]))
(defn sign-later-popup
[from-chat?]
@ -192,8 +194,9 @@
(when advanced?
[advanced-cartouche transaction modal?])])
(defn- send-transaction-panel [{:keys [modal? transaction scroll advanced? symbol]}]
(let [{:keys [amount amount-text amount-error signing? to to-name sufficient-funds? in-progress? from-chat?]} transaction
(defn- send-transaction-panel [{:keys [modal? transaction scroll advanced? network]}]
(let [{:keys [amount amount-text amount-error signing? to to-name sufficient-funds? in-progress? from-chat? symbol]} transaction
{:keys [decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)
timeout (atom nil)]
[wallet.components/simple-screen {:avoid-keyboard? (not modal?)
:status-bar-type (if modal? :modal-wallet :wallet)}
@ -219,7 +222,7 @@
:amount-text amount-text
:input-options {:max-length 21
:on-focus (fn [] (when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount %])}}]
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount % symbol decimals])}} token]
[advanced-options advanced? transaction modal? scroll]]]
(if signing?
[signing-buttons
@ -244,16 +247,20 @@
(letsubs [transaction [:wallet.send/transaction]
symbol [:wallet.send/symbol]
advanced? [:wallet.send/advanced?]
network [:get-current-account-network]
scroll (atom nil)]
[send-transaction-panel {:modal? false :transaction transaction :scroll scroll :advanced? advanced? :symbol symbol}]))
[send-transaction-panel {:modal? false :transaction transaction :scroll scroll :advanced? advanced?
:symbol symbol :network network}]))
(defview send-transaction-modal []
(letsubs [transaction [:wallet.send/unsigned-transaction]
symbol [:wallet.send/symbol]
advanced? [:wallet.send/advanced?]
network [:get-current-account-network]
scroll (atom nil)]
(if transaction
[send-transaction-panel {:modal? true :transaction transaction :scroll scroll :advanced? advanced? :symbol symbol}]
[send-transaction-panel {:modal? true :transaction transaction :scroll scroll :advanced? advanced?
symbol symbol :network network}]
[react/view wallet.styles/wallet-modal-container
[react/view components.styles/flex
[status-bar/status-bar {:type :modal-wallet}]

View File

@ -10,7 +10,9 @@
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.screens.wallet.transactions.styles :as styles]
[status-im.utils.money :as money]))
[status-im.utils.money :as money]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.ethereum.core :as ethereum]))
(defn history-action [filter?]
(cond->
@ -44,12 +46,13 @@
(:postponed :pending) (transaction-icon :icons/arrow-right components.styles/color-gray4-transparent components.styles/color-gray7)
(throw (str "Unknown transaction type: " k))))
(defn render-transaction [{:keys [hash from-contact to-contact to from type value time-formatted symbol] :as transaction}]
(defn render-transaction [{:keys [hash from-contact to-contact to from type value time-formatted symbol] :as transaction} network]
(let [[label contact address
contact-accessibility-label
address-accessibility-label] (if (inbound? type)
[(i18n/label :t/from) from-contact from :sender-text :sender-address-text]
[(i18n/label :t/to) to-contact to :recipient-name-text :recipient-address-text])]
[(i18n/label :t/to) to-contact to :recipient-name-text :recipient-address-text])
{:keys [decimals]} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)]
[list/touchable-item #(re-frame/dispatch [:show-transaction-details hash])
[react/view {:accessibility-label :transaction-item}
[list/item
@ -60,7 +63,7 @@
:ellipsize-mode "tail"
:number-of-lines 1}
[react/text {:accessibility-label :amount-text}
(->> value (money/wei-> :eth) money/to-fixed str)]
(-> value (money/internal->formatted symbol decimals) money/to-fixed str)]
" "
[react/text {:accessibility-label :currency-text}
(clojure.string/upper-case (name symbol))]]
@ -91,11 +94,12 @@
(defview history-list []
(letsubs [transactions-history-list [:wallet.transactions/transactions-history-list]
filter-data [:wallet.transactions/filters]]
filter-data [:wallet.transactions/filters]
network [:get-current-account-network]]
[react/view components.styles/flex
[list/section-list {:sections (map #(update-transactions % filter-data) transactions-history-list)
:key-fn :hash
:render-fn render-transaction
:render-fn #(render-transaction % network)
:empty-component [react/text {:style styles/empty-text}
(i18n/label :t/transactions-history-empty)]
:on-refresh #(re-frame/dispatch [:update-transactions])

View File

@ -380,30 +380,26 @@
:symbol :STT
:decimals 18
:address "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"}
{:name "Aragon Test Token"
:symbol :ATT
:decimals 1
:address "0x00a8e52df8f4f1f4b67bded9ae6090b35489a973"}
{:name "Handy Test Token"
:symbol :HND
:decimals 18
:address "0x9e47fb3049f0d9c953f5428ce2e6c3a8321780bf"}
:decimals 0
:address "0xdee43a267e8726efd60c2e7d5b81552dcd4fa35c"}
{:name "Lucky XS Test"
:symbol :LXS
:decimals 18
:address "0xf29d2dc0687d7d49f57d4a731ac8bfb6edc23473"}
:decimals 2
:address "0x703d7dc0bc8e314d65436adf985dda51e09ad43b"}
{:name "Adi Test Token"
:symbol :ADI
:decimals 18
:address "0xd2a816110c1177478c7e644ae4853d8e80aaec35"}
:decimals 7
:address "0xe639e24346d646e927f323558e6e0031bfc93581"}
{:name "Wagner Test Token"
:symbol :WGN
:decimals 18
:address "0x65c69bc258afa0906683f42e576b272a95c203dd"}
:decimals 10
:address "0x2e7cd05f437eb256f363417fd8f920e2efa77540"}
{:name "Modest Test Token"
:symbol :MDS
:decimals 18
:address "0x972b0570d9cd8b7c41aa8349f707ec7356daa825"}])})
:address "0x57cc9b83730e6d22b224e9dc3e370967b44a2de0"}])})
(defn tokens-for [chain]
(get all chain))

View File

@ -90,6 +90,32 @@
(when-let [bn (bignumber n)]
(.dividedBy bn (bignumber (from-decimal decimals)))))
(defn unit->token [n decimals]
(when-let [bn (bignumber n)]
(.times bn (bignumber (from-decimal decimals)))))
;;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 (bignumber gas) (bignumber gas-price)))