From 36b213985a5268d6db47941d2f563b5857f02050 Mon Sep 17 00:00:00 2001 From: Brian Sztamfater Date: Wed, 22 Nov 2023 17:24:30 -0300 Subject: [PATCH] feat: implement getSuggestedRoutes in the wallet Send Flow Signed-off-by: Brian Sztamfater --- .../components/wallet/token_input/view.cljs | 2 +- src/status_im/multiaccounts/create/core.cljs | 3 +- .../standard_auth/authorize.cljs | 3 +- src/status_im2/constants.cljs | 4 + .../contexts/wallet/common/utils.cljs | 9 +- .../contexts/wallet/send/events.cljs | 78 +++++++++++ .../wallet/send/input_amount/view.cljs | 125 +++++++++++------- .../wallet/send/select_address/view.cljs | 2 +- .../wallet/send/select_asset/view.cljs | 9 +- src/status_im2/events.cljs | 1 + src/status_im2/subs/root.cljs | 1 - src/status_im2/subs/wallet/wallet.cljs | 4 +- src/utils/money.cljs | 8 +- 13 files changed, 184 insertions(+), 65 deletions(-) create mode 100644 src/status_im2/contexts/wallet/send/events.cljs diff --git a/src/quo/components/wallet/token_input/view.cljs b/src/quo/components/wallet/token_input/view.cljs index 9a6ff16aa06c..66b6896bb6e1 100644 --- a/src/quo/components/wallet/token_input/view.cljs +++ b/src/quo/components/wallet/token_input/view.cljs @@ -43,7 +43,7 @@ :align-items :flex-end}} [rn/image {:style style/token - :source (resources/get-token token)}] + :source (resources/get-token (keyword (string/lower-case token)))}] [rn/text-input (cond-> {:auto-focus true :ref #(reset! input-ref %) diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index bbb918c40275..02041b206b80 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -172,8 +172,7 @@ :use-mailservers? true :recovered recovered} config/default-multiaccount) - ;; The address from which we derive any chat - ;; account/encryption keys + ;; The address from which we derive any chat account/encryption keys eip1581-address (assoc :eip1581-address eip1581-address) save-mnemonic? diff --git a/src/status_im2/common/standard_authentication/standard_auth/authorize.cljs b/src/status_im2/common/standard_authentication/standard_auth/authorize.cljs index 0f518d6775a7..077fa077c710 100644 --- a/src/status_im2/common/standard_authentication/standard_auth/authorize.cljs +++ b/src/status_im2/common/standard_authentication/standard_auth/authorize.cljs @@ -30,7 +30,8 @@ :shell? blur? :content (fn [] [enter-password/view - {:on-enter-password on-enter-password}])}]))}) + {:button-label auth-button-label + :on-enter-password on-enter-password}])}]))}) (do (reset-password) (rf/dispatch [:show-bottom-sheet diff --git a/src/status_im2/constants.cljs b/src/status_im2/constants.cljs index b0e20c11da56..38d1b41d4fde 100644 --- a/src/status_im2/constants.cljs +++ b/src/status_im2/constants.cljs @@ -390,3 +390,7 @@ (def ^:const account-default-customization-color :blue) (def ^:const wallet-account-name-max-length 20) + +(def ^:const gas-rate-low 0) +(def ^:const gas-rate-medium 1) +(def ^:const gas-rate-high 2) diff --git a/src/status_im2/contexts/wallet/common/utils.cljs b/src/status_im2/contexts/wallet/common/utils.cljs index c7a6f9cb73bf..3fdc6bd6ed5e 100644 --- a/src/status_im2/contexts/wallet/common/utils.cljs +++ b/src/status_im2/contexts/wallet/common/utils.cljs @@ -43,12 +43,15 @@ (when balance-in-chain (calculate-raw-balance (:raw-balance balance-in-chain) decimals)))) +(defn calculate-balance-for-token + [token] + (* (total-token-value-in-all-chains token) + (-> token :market-values-per-currency :usd :price))) + (defn calculate-balance [tokens-in-account] (->> tokens-in-account - (map (fn [token] - (* (total-token-value-in-all-chains token) - (-> token :market-values-per-currency :usd :price)))) + (map #(calculate-balance-for-token %)) (reduce +))) (defn network-list diff --git a/src/status_im2/contexts/wallet/send/events.cljs b/src/status_im2/contexts/wallet/send/events.cljs new file mode 100644 index 000000000000..e1c5b47408e9 --- /dev/null +++ b/src/status_im2/contexts/wallet/send/events.cljs @@ -0,0 +1,78 @@ +(ns status-im2.contexts.wallet.send.events + (:require + [status-im2.constants :as constants] + [taoensso.timbre :as log] + [utils.datetime :as datetime] + [utils.money :as money] + [utils.number] + [utils.re-frame :as rf])) + +(rf/reg-event-fx :wallet/suggested-routes-success + (fn [{:keys [db]} [suggested-routes timestamp]] + (when (= (get-in db [:wallet :ui :send :suggested-routes-call-timestamp]) timestamp) + {:db (-> db + (assoc-in [:wallet :ui :send :suggested-routes] suggested-routes) + (assoc-in [:wallet :ui :send :route] (first (:Best suggested-routes))) + (assoc-in [:wallet :ui :send :loading-suggested-routes?] false))}))) + +(rf/reg-event-fx :wallet/suggested-routes-error + (fn [{:keys [db]} [_error]] + {:db (-> db + (update-in [:wallet :ui :send] dissoc :suggested-routes) + (update-in [:wallet :ui :send] dissoc :route) + (assoc-in [:wallet :ui :send :loading-suggested-routes?] false))})) + +(rf/reg-event-fx :wallet/clean-suggested-routes + (fn [{:keys [db]}] + {:db (-> db + (update-in [:wallet :ui :send] dissoc :suggested-routes) + (update-in [:wallet :ui :send] dissoc :route) + (update-in [:wallet :ui :send] dissoc :loading-suggested-routes?))})) + +(rf/reg-event-fx :wallet/select-send-address + (fn [{:keys [db]} [address]] + {:db (assoc-in db [:wallet :ui :send :to-address] address)})) + +(rf/reg-event-fx :wallet/send-select-token + (fn [{:keys [db]} [token stack-id]] + {:db (assoc-in db [:wallet :ui :send :token] token) + :fx [[:navigate-to-within-stack [:wallet-send-input-amount stack-id]]]})) + +(rf/reg-event-fx :wallet/get-suggested-routes + (fn [{:keys [db]} [amount]] + (let [wallet-address (get-in db [:wallet :current-viewing-account-address]) + token (get-in db [:wallet :ui :send :token]) + to-address (get-in db [:wallet :ui :send :to-address]) + token-decimal (:decimals token) + token-id (:symbol token) + network-preferences [constants/mainnet-chain-id] ; TODO: don't hardcode network preferences + gas-rates constants/gas-rate-low + amount-in (money/mul (money/bignumber amount) + (money/from-decimal token-decimal)) + from-address wallet-address + request-params [0 + from-address + to-address + (money/to-hex amount-in) + token-id + [] + [] + network-preferences + gas-rates + {}] + timestamp (datetime/timestamp)] + {:db (-> db + (assoc-in [:wallet :ui :send :loading-suggested-routes?] true) + (assoc-in [:wallet :ui :send :suggested-routes-call-timestamp] + timestamp)) + :json-rpc/call [{:method "wallet_getSuggestedRoutes" + :params request-params + :on-success (fn [suggested-routes] + (rf/dispatch [:wallet/suggested-routes-success suggested-routes + timestamp])) + :on-error (fn [error] + (rf/dispatch [:wallet/suggested-routes-error error]) + (log/error "failed to get suggested routes" + {:event :wallet/get-suggested-routes + :error error + :params request-params}))}]}))) diff --git a/src/status_im2/contexts/wallet/send/input_amount/view.cljs b/src/status_im2/contexts/wallet/send/input_amount/view.cljs index 1c0294146e01..e960c5e7caca 100644 --- a/src/status_im2/contexts/wallet/send/input_amount/view.cljs +++ b/src/status_im2/contexts/wallet/send/input_amount/view.cljs @@ -7,6 +7,7 @@ [react-native.safe-area :as safe-area] [reagent.core :as reagent] [status-im2.contexts.wallet.send.input-amount.style :as style] + [utils.debounce :as debounce] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -47,71 +48,88 @@ current)) (defn- f-view-internal - [{:keys [token limit rate]}] - (let [bottom (safe-area/get-bottom) - {:keys [currency]} (rf/sub [:profile/profile]) - networks (rf/sub [:wallet/network-details]) - ;; Temporary values - token (or token :eth) - conversion-rate (or rate 10) - limit-crypto (or limit 2860000.32) - limit-fiat (* limit-crypto conversion-rate) - input-value (reagent/atom "") - current-limit (reagent/atom {:amount limit-crypto - :currency token}) - handle-swap (fn [crypto?] - (let [num-value (parse-double @input-value)] - (reset! current-limit (if crypto? - {:amount limit-crypto - :currency token} - {:amount limit-fiat - :currency currency})) - (when (> num-value (:amount @current-limit)) - (reset! input-value "")))) - handle-keyboard-press (fn [v] - (let [current-value @input-value - new-value (make-new-input current-value v) - num-value (or (parse-double new-value) 0)] - (when (<= num-value (:amount @current-limit)) - (reset! input-value new-value) - (reagent/flush)))) - handle-delete (fn [_] - (swap! input-value #(subs % 0 (dec (count %)))) - (reagent/flush)) - handle-on-change (fn [v] - (when (valid-input? @input-value v) - (let [num-value (or (parse-double v) 0) - current-limit-amount (:amount @current-limit)] - (if (> num-value current-limit-amount) - (reset! input-value (str current-limit-amount)) - (reset! input-value v)) - (reagent/flush))))] + [{:keys [rate]}] + (let [bottom (safe-area/get-bottom) + {:keys [currency]} (rf/sub [:profile/profile]) + networks (rf/sub [:wallet/network-details]) + wallet-send (get-in (rf/sub [:wallet]) [:ui :send]) + token (:token wallet-send) + token-symbol (:symbol token) + limit-crypto (:total-balance token) + limit-fiat (:total-balance-fiat token) + conversion-rate (or rate 10) + input-value (reagent/atom "") + current-limit (reagent/atom {:amount limit-crypto + :currency token-symbol}) + loading-suggested-routes? (:loading-suggested-routes? wallet-send) + handle-swap (fn [crypto?] + (let [num-value (parse-double @input-value)] + (reset! current-limit (if crypto? + {:amount limit-crypto + :currency token-symbol} + {:amount limit-fiat + :currency currency})) + (when (> num-value (:amount @current-limit)) + (reset! input-value "")))) + handle-keyboard-press (fn [v] + (let [current-value @input-value + new-value (make-new-input current-value v) + num-value (or (parse-double new-value) 0)] + (when (and (not loading-suggested-routes?) + (<= num-value (:amount @current-limit))) + (reset! input-value new-value) + (reagent/flush)))) + handle-delete (fn [_] + (when-not loading-suggested-routes? + (swap! input-value #(subs % 0 (dec (count %)))) + (reagent/flush))) + handle-on-change (fn [v] + (when (valid-input? @input-value v) + (let [num-value (or (parse-double v) 0) + current-limit-amount (:amount @current-limit)] + (if (> num-value current-limit-amount) + (reset! input-value (str current-limit-amount)) + (reset! input-value v)) + (reagent/flush))))] (fn [{:keys [on-confirm] - :or {on-confirm #(js/alert "Confirmed")}}] - (let [limit-label (make-limit-label @current-limit) - input-num-value (parse-double @input-value) - confirm-disabled? (or - (empty? @input-value) - (<= input-num-value 0) - (> input-num-value (:amount @current-limit)))] + :or {on-confirm #(rf/dispatch [:wallet/send-select-amount @input-value + :wallet-send-input-amount])}}] + (let [limit-label (make-limit-label @current-limit) + input-num-value (parse-double @input-value) + route (get-in (rf/sub [:wallet]) [:ui :send :route]) + loading-suggested-routes? (get-in (rf/sub [:wallet]) [:ui :send :loading-suggested-routes?]) + confirm-disabled? (or + (nil? route) + (empty? @input-value) + (<= input-num-value 0) + (> input-num-value (:amount @current-limit)))] (rn/use-effect (fn [] (let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!)) app-keyboard-listener (.addEventListener rn/app-state "change" dismiss-keyboard-fn)] #(.remove app-keyboard-listener)))) + (rn/use-effect (fn [] + (rf/dispatch [:wallet/clean-suggested-routes]) + (when-not (or + (empty? @input-value) + (<= input-num-value 0) + (> input-num-value (:amount @current-limit))) + (debounce/debounce-and-dispatch [:wallet/get-suggested-routes @input-value] + 100))) + [@input-value]) [rn/view {:style style/screen} [quo/page-nav {:background :blur :icon-name :i/arrow-left - :on-press #(rf/dispatch [:navigate-back]) + :on-press #(rf/dispatch [:navigate-back-within-stack :wallet-send-input-amount]) :right-side :account-switcher :account-switcher {:customization-color :yellow :emoji "🎮" :on-press #(js/alert "Switch account")}}] [quo/token-input {:container-style style/input-container - :token token + :token token-symbol :currency currency :networks networks :title (i18n/label :t/send-limit {:limit limit-label}) @@ -122,7 +140,16 @@ :on-change-text (fn [text] (handle-on-change text))}] ;; Network routing content to be added - [rn/scroll-view] + [rn/scroll-view + {:content-container-style {:flex-grow 1 + :align-items :center + :justify-content :center}} + (cond loading-suggested-routes? + [quo/text "Loading routes"] + (and (not loading-suggested-routes?) route) + [quo/text "Route found"] + (and (not loading-suggested-routes?) (nil? route)) + [quo/text "Route not found"])] [quo/bottom-actions {:actions :1-action :button-one-label (i18n/label :t/confirm) diff --git a/src/status_im2/contexts/wallet/send/select_address/view.cljs b/src/status_im2/contexts/wallet/send/select_address/view.cljs index 20d365c06bf1..2c8d394047ae 100644 --- a/src/status_im2/contexts/wallet/send/select_address/view.cljs +++ b/src/status_im2/contexts/wallet/send/select_address/view.cljs @@ -48,7 +48,7 @@ [input-value input-focused?] (fn [] (let [scanned-address (rf/sub [:wallet/scanned-address]) - send-address (rf/sub [:wallet/send-address]) + send-address (get-in (rf/sub [:wallet]) [:ui :send :to-address]) valid-ens-or-address? (rf/sub [:wallet/valid-ens-or-address?])] [quo/address-input {:on-focus (fn [] diff --git a/src/status_im2/contexts/wallet/send/select_asset/view.cljs b/src/status_im2/contexts/wallet/send/select_asset/view.cljs index 984dda3338e1..4fa39442523d 100644 --- a/src/status_im2/contexts/wallet/send/select_asset/view.cljs +++ b/src/status_im2/contexts/wallet/send/select_asset/view.cljs @@ -18,10 +18,13 @@ (defn- asset-component [] (fn [token _ _ _] - (let [on-press #(js/alert "Not implemented yet") + (let [on-press + #(if (= (:symbol token) "ETH") + (rf/dispatch [:wallet/send-select-token token :wallet-select-asset]) + (js/alert "Only ETH transfers are allowed.")) total-balance-formatted (.toFixed (:total-balance token) 2) - balance-fiat-formatted (.toFixed (:total-balance-fiat token) 2) - currency-symbol "$"] + balance-fiat-formatted (.toFixed (:total-balance-fiat token) 2) + currency-symbol "$"] [quo/token-network {:token (quo.resources/get-token (keyword (string/lower-case (:symbol token)))) :label (:name token) diff --git a/src/status_im2/events.cljs b/src/status_im2/events.cljs index 8715692f4944..6e489561d5b5 100644 --- a/src/status_im2/events.cljs +++ b/src/status_im2/events.cljs @@ -22,6 +22,7 @@ status-im2.contexts.shell.share.events status-im2.contexts.syncing.events status-im2.contexts.wallet.events + status-im2.contexts.wallet.send.events [status-im2.db :as db] [utils.re-frame :as rf])) diff --git a/src/status_im2/subs/root.cljs b/src/status_im2/subs/root.cljs index 19100c2f7a5f..f33c65b97e42 100644 --- a/src/status_im2/subs/root.cljs +++ b/src/status_im2/subs/root.cljs @@ -156,7 +156,6 @@ (reg-root-key-sub :wallet/scanned-address :wallet/scanned-address) (reg-root-key-sub :wallet/local-suggestions :wallet/local-suggestions) (reg-root-key-sub :wallet/valid-ens-or-address? :wallet/valid-ens-or-address?) -(reg-root-key-sub :wallet/send-address :wallet/send-address) ;;debug (when js/goog.DEBUG diff --git a/src/status_im2/subs/wallet/wallet.cljs b/src/status_im2/subs/wallet/wallet.cljs index 2f7fe983bae5..6cc1bfa2d663 100644 --- a/src/status_im2/subs/wallet/wallet.cljs +++ b/src/status_im2/subs/wallet/wallet.cljs @@ -48,7 +48,7 @@ :<- [:wallet/balances] :<- [:wallet/tokens-loading?] (fn [[accounts balances tokens-loading?]] - (mapv (fn [{:keys [color address type] :as account}] + (mapv (fn [{:keys [color address] :as account}] (assoc account :customization-color color :type (if (= type :watch) :watch-only :empty) @@ -75,7 +75,7 @@ (assoc token :networks (utils/network-list token networks) :total-balance (utils/total-token-value-in-all-chains token) - :total-balance-fiat (utils/calculate-balance token))) + :total-balance-fiat (utils/calculate-balance-for-token token))) (:tokens account)) sorted-tokens diff --git a/src/utils/money.cljs b/src/utils/money.cljs index 68e5a07baa5c..e70bb4eb1450 100644 --- a/src/utils/money.cljs +++ b/src/utils/money.cljs @@ -152,7 +152,7 @@ ;; 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 +;; Different tokens can have different number of allowed decimals, so it's necessary to include the ;; decimals parameter ;; to get the amount scale right. @@ -205,10 +205,14 @@ (with-precision 2) str)) -(defn add +(defn- add* [bn1 n2] (.add ^js bn1 n2)) +(def add + "Add with defaults, this version is able to receive `nil` and takes them as 0." + (fnil add* (bignumber 0) (bignumber 0))) + (defn mul [bn1 bn2] (.mul ^js bn1 bn2))