From a7d330ecb3477e3d2ade7989dd9185b929541b55 Mon Sep 17 00:00:00 2001 From: Brian Sztamfater Date: Sun, 10 Nov 2024 20:16:11 -0300 Subject: [PATCH] fix(swap): refetch swap proposal when switching account (#21518) Signed-off-by: Brian Sztamfater --- .../contexts/wallet/common/utils.cljs | 12 +++ .../contexts/wallet/common/utils_test.cljs | 74 +++++++++++++++++++ .../wallet/sheets/select_account/view.cljs | 12 ++- .../contexts/wallet/swap/events.cljs | 7 +- .../contexts/wallet/swap/setup_swap/view.cljs | 65 ++++++++-------- src/status_im/subs/wallet/swap.cljs | 49 ++++++++++++ 6 files changed, 176 insertions(+), 43 deletions(-) diff --git a/src/status_im/contexts/wallet/common/utils.cljs b/src/status_im/contexts/wallet/common/utils.cljs index 1c99ad1a3d..3c5bf2b965 100644 --- a/src/status_im/contexts/wallet/common/utils.cljs +++ b/src/status_im/contexts/wallet/common/utils.cljs @@ -192,6 +192,18 @@ (number/small-number-threshold display-decimals) (str amount-fixed-decimals)))) +(defn token-balance-display-for-network + "Formats a token balance for a specific chain and rounds it to a specified number of decimals. + If the balance is less than the smallest representable value based on rounding decimals, + a threshold value is displayed instead." + [token chain-id rounding-decimals] + (let [token-decimals (:decimals token) + display-decimals (min token-decimals rounding-decimals)] + (-> (get-in token [:balances-per-chain chain-id :raw-balance] 0) + (number/convert-to-whole-number token-decimals) + money/bignumber + (sanitized-token-amount-to-display display-decimals)))) + (defn calculate-balance-from-tokens [{:keys [currency tokens chain-ids]}] (->> tokens diff --git a/src/status_im/contexts/wallet/common/utils_test.cljs b/src/status_im/contexts/wallet/common/utils_test.cljs index 6403840ed5..ae80a47b2e 100644 --- a/src/status_im/contexts/wallet/common/utils_test.cljs +++ b/src/status_im/contexts/wallet/common/utils_test.cljs @@ -195,3 +195,77 @@ (is (= (utils/sanitized-token-amount-to-display 0.00000123 6) "0.000001")) (is (= (utils/sanitized-token-amount-to-display nil 2) "0")) (is (= (utils/sanitized-token-amount-to-display "invalid" 2) "0")))) + +(deftest token-balance-display-for-network-test + (testing "Standard balance with rounding decimals" + (let [token {:decimals 18 + :balances-per-chain {1 {:raw-balance "1000000000000000000"}}} + chain-id 1 + rounding-decimals 2 + expected "1"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected)))) + + (testing "Balance with more decimals than specified rounding" + (let [token {:decimals 18 + :balances-per-chain {1 {:raw-balance "123456789000000000"}}} + chain-id 1 + rounding-decimals 3 + expected "0.123"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected)))) + + (testing "Very small balance displayed as threshold (<0.000001)" + (let [token {:decimals 18 + :balances-per-chain {1 {:raw-balance "1"}}} + chain-id 1 + rounding-decimals 6 + expected "<0.000001"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected)))) + + (testing + "Very small balance displayed without threshold when token decimals are equal than reounding decimals" + (let [token {:decimals 18 + :balances-per-chain {1 {:raw-balance "1"}}} + chain-id 1 + rounding-decimals 18 + expected "0.000000000000000001"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected)))) + + (testing + "Very small balance displayed without threshold when token decimals are lower than reounding decimals" + (let [token {:decimals 18 + :balances-per-chain {1 {:raw-balance "1"}}} + chain-id 1 + rounding-decimals 21 + expected "0.000000000000000001"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected)))) + + (testing "Zero balance displayed correctly" + (let [token {:decimals 18 + :balances-per-chain {1 {:raw-balance "0"}}} + chain-id 1 + rounding-decimals 2 + expected "0"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected)))) + + (testing "Balance with trailing zeroes removed" + (let [token {:decimals 8 + :balances-per-chain {1 {:raw-balance "123400000"}}} + chain-id 1 + rounding-decimals 6 + expected "1.234"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected)))) + + (testing "Balance when chain data is missing" + (let [token {:decimals 18} + chain-id 1 + rounding-decimals 2 + expected "0"] + (is (= (utils/token-balance-display-for-network token chain-id rounding-decimals) + expected))))) diff --git a/src/status_im/contexts/wallet/sheets/select_account/view.cljs b/src/status_im/contexts/wallet/sheets/select_account/view.cljs index 7df0050f31..6a839dba16 100644 --- a/src/status_im/contexts/wallet/sheets/select_account/view.cljs +++ b/src/status_im/contexts/wallet/sheets/select_account/view.cljs @@ -2,11 +2,13 @@ (:require [quo.core :as quo] quo.theme [react-native.gesture :as gesture] - [status-im.contexts.wallet.common.utils :as wallet.utils] + [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.sheets.select-account.style :as style] [utils.i18n :as i18n] [utils.re-frame :as rf])) +(def ^:private rounding-decimals 4) + (defn list-item [{:keys [account selected-account-address]}] (let [{:keys [color address]} account] @@ -25,12 +27,14 @@ token (->> tokens (filter #(= (:symbol %) asset-symbol)) first) - token-balance (get-in token [:balances-per-chain (:chain-id network) :balance])] + chain-id (:chain-id network) + token-balance-display (utils/token-balance-display-for-network token + chain-id + rounding-decimals)] [quo/account-item {:type (if (= address selected-account-address) :default :tag) :token-props {:symbol asset-symbol - :value (wallet.utils/cut-fiat-balance (or (js/parseFloat token-balance) 0) - 4)} + :value token-balance-display} :account-props (assoc account :customization-color color) :customization-color color :state (if (= address selected-account-address) :selected :default) diff --git a/src/status_im/contexts/wallet/swap/events.cljs b/src/status_im/contexts/wallet/swap/events.cljs index 84d975403d..a0761665fa 100644 --- a/src/status_im/contexts/wallet/swap/events.cljs +++ b/src/status_im/contexts/wallet/swap/events.cljs @@ -247,13 +247,12 @@ (rf/reg-event-fx :wallet/clean-swap-proposal - (fn [{:keys [db]} [{:keys [clean-approval-transaction?]}]] - (let [keys-to-dissoc (cond-> [:amount - :amount-hex - :last-request-uuid + (fn [{:keys [db]} [{:keys [clean-amounts? clean-approval-transaction?]}]] + (let [keys-to-dissoc (cond-> [:last-request-uuid :swap-proposal :error-response :loading-swap-proposal?] + clean-amounts? (conj :amount :amount-hex) clean-approval-transaction? (conj :approval-transaction-id :approved-amount))] {:db (apply update-in db [:wallet :ui :swap] dissoc keys-to-dissoc) :fx [[:dispatch [:wallet/stop-get-swap-proposal]]]}))) diff --git a/src/status_im/contexts/wallet/swap/setup_swap/view.cljs b/src/status_im/contexts/wallet/swap/setup_swap/view.cljs index 7564474250..220ebd8fc3 100644 --- a/src/status_im/contexts/wallet/swap/setup_swap/view.cljs +++ b/src/status_im/contexts/wallet/swap/setup_swap/view.cljs @@ -26,13 +26,14 @@ [utils.string :as utils.string])) (def ^:private default-text-for-unfocused-input "0.00") -(def ^:private default-token-symbol "ETH") (defn- on-close [start-point] (when (= start-point :action-menu) (rf/dispatch [:centralized-metrics/track :metric/swap-closed])) - (rf/dispatch [:wallet/clean-swap-proposal {:clean-approval-transaction? true}]) + (rf/dispatch [:wallet/clean-swap-proposal + {:clean-amounts? true + :clean-approval-transaction? true}]) (events-helper/navigate-back)) (defn- fetch-swap-proposal @@ -43,7 +44,8 @@ :clean-approval-transaction? clean-approval-transaction?}] 100) (rf/dispatch [:wallet/clean-swap-proposal - {:clean-approval-transaction? clean-approval-transaction?}]))) + {:clean-amounts? true + :clean-approval-transaction? clean-approval-transaction?}]))) (defn- data-item [{:keys [title subtitle size subtitle-icon subtitle-color on-press loading?]}] @@ -90,47 +92,31 @@ [{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}] (let [account-color (rf/sub [:wallet/current-viewing-account-color]) network (rf/sub [:wallet/swap-network]) - pay-token-symbol (:symbol (rf/sub [:wallet/swap-asset-to-pay])) - currency (rf/sub [:profile/currency]) + pay-token-symbol (rf/sub [:wallet/swap-asset-to-pay-symbol]) + pay-token-decimals (rf/sub [:wallet/swap-asset-to-pay-decimals]) loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?]) swap-proposal (rf/sub [:wallet/swap-proposal-without-fees]) approval-required (rf/sub [:wallet/swap-proposal-approval-required]) approval-amount-required (rf/sub [:wallet/swap-proposal-approval-amount-required]) - currency-symbol (rf/sub [:profile/currency-symbol]) approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status]) approval-transaction-id (rf/sub [:wallet/swap-approval-transaction-id]) approved-amount (rf/sub [:wallet/swap-approved-amount]) error-response (rf/sub [:wallet/swap-error-response]) - asset-to-pay (rf/sub [:wallet/token-by-symbol - (or pay-token-symbol default-token-symbol)]) overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet]))) input-ref (rn/use-ref-atom nil) set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref))) pay-input-num-value (controlled-input/value-numeric input-state) pay-input-amount (controlled-input/input-value input-state) - pay-token-decimals (:decimals asset-to-pay) - pay-token-balance-selected-chain (number/convert-to-whole-number - (get-in asset-to-pay - [:balances-per-chain - (:chain-id network) :raw-balance] - 0) - pay-token-decimals) - pay-token-fiat-value (utils/formatted-token-fiat-value - {:currency currency - :currency-symbol currency-symbol - :balance (or pay-input-num-value 0) - :token asset-to-pay}) + pay-token-balance-selected-chain (rf/sub [:wallet/swap-asset-to-pay-balance-for-chain + (:chain-id network)]) + pay-token-fiat-value (rf/sub [:wallet/swap-asset-to-pay-amount-in-fiat + pay-input-num-value]) available-crypto-limit (money/bignumber pay-token-balance-selected-chain) display-decimals (min pay-token-decimals constants/min-token-decimals-to-display) - available-crypto-limit-display (number/remove-trailing-zeroes - (.toFixed available-crypto-limit display-decimals)) - available-crypto-limit-display (if (and (= available-crypto-limit-display "0") - (money/greater-than available-crypto-limit - (money/bignumber 0))) - (number/small-number-threshold display-decimals) - available-crypto-limit-display) + available-crypto-limit-display (rf/sub [:wallet/swap-asset-to-pay-balance-for-chain-ui + (:chain-id network)]) approval-amount-required-num (when approval-amount-required (number/to-fixed (number/hex->whole approval-amount-required @@ -345,15 +331,12 @@ asset-to-pay (rf/sub [:wallet/swap-asset-to-pay]) asset-to-receive (rf/sub [:wallet/swap-asset-to-receive]) network (rf/sub [:wallet/swap-network]) + pay-token-balance-selected-chain (rf/sub [:wallet/swap-asset-to-pay-balance-for-chain + (:chain-id network)]) start-point (rf/sub [:wallet/swap-start-point]) + current-account-address (rf/sub [:wallet/current-viewing-account-address]) pay-input-amount (controlled-input/input-value pay-input-state) pay-token-decimals (:decimals asset-to-pay) - pay-token-balance-selected-chain (number/convert-to-whole-number - (get-in asset-to-pay - [:balances-per-chain - (:chain-id network) :raw-balance] - 0) - pay-token-decimals) pay-input-error? (and (not (string/blank? pay-input-amount)) (money/greater-than (money/bignumber pay-input-amount) @@ -392,7 +375,8 @@ (set-pay-input-state controlled-input/delete-last) (rf/dispatch [:wallet/clean-swap-proposal - {:clean-approval-transaction? + {:clean-amounts? true + :clean-approval-transaction? true}]))) on-max-press (rn/use-callback (fn [max-value] @@ -443,8 +427,19 @@ on-refresh-swap-proposal constants/swap-proposal-refresh-interval-ms)))) [swap-proposal error-response]) + (rn/use-effect (fn [] + (rf/dispatch [:wallet/clean-swap-proposal + {:clean-amounts? false + :clean-approval-transaction? true}]) + (when @refetch-interval + (js/clearInterval @refetch-interval) + (reset! refetch-interval nil)) + (refetch-swap-proposal)) + [current-account-address]) (rn/use-unmount (fn [] - (rf/dispatch [:wallet/clean-swap-proposal {:clean-approval-transaction? true}]) + (rf/dispatch [:wallet/clean-swap-proposal + {:clean-amounts? true + :clean-approval-transaction? true}]) (when @refetch-interval (js/clearInterval @refetch-interval) (reset! refetch-interval nil)))) diff --git a/src/status_im/subs/wallet/swap.cljs b/src/status_im/subs/wallet/swap.cljs index 56dd355404..95e1716a67 100644 --- a/src/status_im/subs/wallet/swap.cljs +++ b/src/status_im/subs/wallet/swap.cljs @@ -17,6 +17,16 @@ :<- [:wallet/swap] :-> :asset-to-pay) +(rf/reg-sub + :wallet/swap-asset-to-pay-decimals + :<- [:wallet/swap-asset-to-pay] + :-> :decimals) + +(rf/reg-sub + :wallet/swap-asset-to-pay-symbol + :<- [:wallet/swap-asset-to-pay] + :-> :symbol) + (rf/reg-sub :wallet/swap-asset-to-receive :<- [:wallet/swap] @@ -253,3 +263,42 @@ currency-symbol fee-in-fiat)] fee-formatted)))) + +(rf/reg-sub + :wallet/swap-asset-to-pay-balance-for-chain-data + :<- [:wallet/swap-asset-to-pay] + (fn [asset-to-pay] + (let [token-symbol (or (:symbol asset-to-pay) constants/token-for-fees-symbol)] + @(rf/subscribe [:wallet/token-by-symbol token-symbol])))) + +(rf/reg-sub + :wallet/swap-asset-to-pay-balance-for-chain + :<- [:wallet/swap-asset-to-pay-balance-for-chain-data] + (fn [asset-to-pay-with-current-account-balance [_ chain-id]] + (let [pay-token-decimals (:decimals asset-to-pay-with-current-account-balance) + pay-token-balance-selected-chain (-> (get-in asset-to-pay-with-current-account-balance + [:balances-per-chain chain-id :raw-balance] + 0) + (number/convert-to-whole-number pay-token-decimals))] + pay-token-balance-selected-chain))) + +(rf/reg-sub + :wallet/swap-asset-to-pay-balance-for-chain-ui + :<- [:wallet/swap-asset-to-pay-balance-for-chain-data] + (fn [asset-to-pay-with-current-account-balance [_ chain-id]] + (utils/token-balance-display-for-network + asset-to-pay-with-current-account-balance + chain-id + constants/min-token-decimals-to-display))) + +(rf/reg-sub + :wallet/swap-asset-to-pay-amount-in-fiat + :<- [:wallet/swap-asset-to-pay-balance-for-chain-data] + :<- [:profile/currency] + :<- [:profile/currency-symbol] + (fn [[asset-to-pay-with-current-account-balance currency currency-symbol] [_ amount]] + (utils/formatted-token-fiat-value + {:currency currency + :currency-symbol currency-symbol + :balance (or amount 0) + :token asset-to-pay-with-current-account-balance})))