From 19aa78cc8164bb15fcfd7ac26c6a4c3701cdce66 Mon Sep 17 00:00:00 2001 From: Omar Basem Date: Wed, 17 Jan 2024 13:57:02 +0400 Subject: [PATCH] Wallet: loading networks (#18491) Wallet: loading networks --- src/status_im/constants.cljs | 3 +- .../wallet/account/tabs/about/view.cljs | 21 ++- .../common/sheets/account_options/view.cljs | 8 +- .../contexts/wallet/common/utils.cljs | 12 ++ .../contexts/wallet/common/validation.cljs | 2 +- .../contexts/wallet/send/events.cljs | 169 ++++++------------ .../wallet/send/input_amount/view.cljs | 14 +- .../contexts/wallet/send/routes/view.cljs | 38 ++-- .../wallet/send/select_address/view.cljs | 4 +- src/status_im/subs/wallet/wallet.cljs | 5 + 10 files changed, 127 insertions(+), 149 deletions(-) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index e67c5512d8..9155c6704a 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -219,7 +219,8 @@ (def regx-community-universal-link #"((^https?://status.app/)|(^status-app://))c/([\x00-\x7F]+)$") (def regx-deep-link #"((^ethereum:.*)|(^status-app://[\x00-\x7F]+$))") (def regx-ens #"^(?=.{5,255}$)([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$") -(def regx-address #"^0x[a-fA-F0-9]{40}$") +(def regx-multichain-address #"^(?:(?:eth:|arb1:|opt:)(?=:|))*0x[0-9a-fA-F]{40}$") + (def regx-address-contains #"(?i)0x[a-fA-F0-9]{40}") (def regx-starts-with-uuid #"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") (def regx-full-or-partial-address #"^0x[a-fA-F0-9]{1,40}$") diff --git a/src/status_im/contexts/wallet/account/tabs/about/view.cljs b/src/status_im/contexts/wallet/account/tabs/about/view.cljs index 0bab2cf3df..2015eadc8a 100644 --- a/src/status_im/contexts/wallet/account/tabs/about/view.cljs +++ b/src/status_im/contexts/wallet/account/tabs/about/view.cljs @@ -8,13 +8,16 @@ [status-im.config :as config] [status-im.contexts.profile.utils :as profile.utils] [status-im.contexts.wallet.account.tabs.about.style :as style] + [status-im.contexts.wallet.common.utils :as utils] [utils.i18n :as i18n] [utils.re-frame :as rf])) (defn about-options [] (let [{:keys [address] :as account} (rf/sub [:wallet/current-viewing-account]) - share-title (str (:name account) " " (i18n/label :t/address))] + networks (rf/sub [:wallet/network-preference-details]) + share-title (str (:name account) " " (i18n/label :t/address)) + multichain-address (utils/get-multichain-address networks address)] [quo/action-drawer [[{:icon :i/link :accessibility-label :view-on-eth @@ -44,7 +47,7 @@ :accessibility-label :copy-address :label (i18n/label :t/copy-address) :on-press (fn [] - (clipboard/set-string address) + (clipboard/set-string multichain-address) (rf/dispatch [:toasts/upsert {:type :positive :text (i18n/label :t/address-copied)}]))} @@ -59,15 +62,17 @@ (js/setTimeout #(share/open (if platform/ios? - {:activityItemSources [{:placeholderItem {:type "text" - :content address} - :item {:default {:type "text" - :content - address}} + {:activityItemSources [{:placeholderItem {:type "text" + :content + multichain-address} + :item {:default + {:type "text" + :content + multichain-address}} :linkMetadata {:title share-title}}]} {:title share-title :subject share-title - :message address})) + :message multichain-address})) 600))}]]])) (defn view diff --git a/src/status_im/contexts/wallet/common/sheets/account_options/view.cljs b/src/status_im/contexts/wallet/common/sheets/account_options/view.cljs index 7b841db709..f5ac6b98ec 100644 --- a/src/status_im/contexts/wallet/common/sheets/account_options/view.cljs +++ b/src/status_im/contexts/wallet/common/sheets/account_options/view.cljs @@ -10,6 +10,7 @@ [react-native.platform :as platform] [reagent.core :as reagent] [status-im.contexts.wallet.common.sheets.account-options.style :as style] + [status-im.contexts.wallet.common.utils :as utils] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -31,7 +32,10 @@ (defn- options [{:keys [theme show-account-selector? options-height]}] (let [{:keys [name color emoji address watch-only?]} (rf/sub [:wallet/current-viewing-account]) - network-preference-details (rf/sub [:wallet/network-preference-details])] + network-preference-details (rf/sub [:wallet/network-preference-details]) + multichain-address (utils/get-multichain-address + network-preference-details + address)] [rn/view {:on-layout #(reset! options-height (oops/oget % "nativeEvent.layout.height")) :style (when show-account-selector? style/options-container)} @@ -72,7 +76,7 @@ (rf/dispatch [:toasts/upsert {:type :positive :text (i18n/label :t/address-copied)}]) - (clipboard/set-string address))} + (clipboard/set-string multichain-address))} {:icon :i/share :accessibility-label :share-account :label (i18n/label :t/share-account)} diff --git a/src/status_im/contexts/wallet/common/utils.cljs b/src/status_im/contexts/wallet/common/utils.cljs index 02e666c824..37c0bc325e 100644 --- a/src/status_im/contexts/wallet/common/utils.cljs +++ b/src/status_im/contexts/wallet/common/utils.cljs @@ -202,3 +202,15 @@ :customization-color color :values {:crypto-value crypto-value :fiat-value fiat-value}})) + +(defn get-multichain-address + [networks address] + (str (->> networks + (map #(str (:short-name %) ":")) + (clojure.string/join "")) + address)) + +(defn split-prefix-and-address + [input-string] + (let [split-result (string/split input-string #"0x")] + [(first split-result) (str "0x" (second split-result))])) diff --git a/src/status_im/contexts/wallet/common/validation.cljs b/src/status_im/contexts/wallet/common/validation.cljs index 2e9ab0d379..9d4d11c6d6 100644 --- a/src/status_im/contexts/wallet/common/validation.cljs +++ b/src/status_im/contexts/wallet/common/validation.cljs @@ -2,4 +2,4 @@ (:require [status-im.constants :as constants])) (defn ens-name? [s] (re-find constants/regx-ens s)) -(defn eth-address? [s] (re-find constants/regx-address s)) +(defn eth-address? [s] (re-find constants/regx-multichain-address s)) diff --git a/src/status_im/contexts/wallet/send/events.cljs b/src/status_im/contexts/wallet/send/events.cljs index 109acfee3d..6ba7c9789e 100644 --- a/src/status_im/contexts/wallet/send/events.cljs +++ b/src/status_im/contexts/wallet/send/events.cljs @@ -2,11 +2,10 @@ (:require [camel-snake-kebab.core :as csk] [camel-snake-kebab.extras :as cske] - [native-module.core :as native-module] [status-im.constants :as constants] + [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.send.utils :as send-utils] [taoensso.timbre :as log] - [utils.address :as address] [utils.money :as money] [utils.number] [utils.re-frame :as rf])) @@ -42,23 +41,32 @@ (update-in [:wallet :ui :send] dissoc :route) (update-in [:wallet :ui :send] dissoc :loading-suggested-routes?))})) +(rf/reg-event-fx :wallet/select-send-account-address + (fn [{:keys [db]} [{:keys [address stack-id]}]] + {:db (-> db + (assoc-in [:wallet :ui :send :send-account-address] address) + (update-in [:wallet :ui :send] dissoc :to-address)) + :fx [[:navigate-to-within-stack [:wallet-select-asset stack-id]]]})) + (rf/reg-event-fx :wallet/clean-send-address (fn [{:keys [db]}] {:db (update-in db [:wallet :ui :send] dissoc :recipient :to-address)})) (rf/reg-event-fx :wallet/select-send-address (fn [{:keys [db]} [{:keys [address token recipient stack-id]}]] - {:db (-> db - (assoc-in [:wallet :ui :send :recipient] (or recipient address)) - (assoc-in [:wallet :ui :send :to-address] address)) - :fx [[:navigate-to-within-stack - (if token [:wallet-send-input-amount stack-id] [:wallet-select-asset stack-id])]]})) + (let [[prefix to-address] (utils/split-prefix-and-address address)] + {:db (-> db + (assoc-in [:wallet :ui :send :recipient] (or recipient address)) + (assoc-in [:wallet :ui :send :to-address] to-address) + (assoc-in [:wallet :ui :send :address-prefix] prefix)) + :fx [[:navigate-to-within-stack + (if token + [:wallet-send-input-amount stack-id] + [:wallet-select-asset stack-id])]]}))) (rf/reg-event-fx :wallet/send-select-token (fn [{:keys [db]} [{:keys [token stack-id]}]] - {:db (-> db - (update-in [:wallet :ui :send] dissoc :collectible) - (assoc-in [:wallet :ui :send :token] token)) + {: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/send-select-token-drawer @@ -69,15 +77,6 @@ (fn [{:keys [db]}] {:db (assoc-in db [:wallet :ui :send :token] nil)})) -(rf/reg-event-fx :wallet/send-select-collectible - (fn [{:keys [db]} [{:keys [collectible stack-id]}]] - {:db (-> db - (update-in [:wallet :ui :send] dissoc :token) - (assoc-in [:wallet :ui :send :collectible] collectible) - (assoc-in [:wallet :ui :send :amount] 1)) - :fx [[:dispatch [:wallet/get-suggested-routes 1]] - [:navigate-to-within-stack [:wallet-transaction-confirmation stack-id]]]})) - (rf/reg-event-fx :wallet/send-select-amount (fn [{:keys [db]} [{:keys [amount stack-id]}]] {:db (assoc-in db [:wallet :ui :send :amount] amount) @@ -87,25 +86,18 @@ (fn [{:keys [db now]} [amount]] (let [wallet-address (get-in db [:wallet :current-viewing-account-address]) token (get-in db [:wallet :ui :send :token]) - collectible (get-in db [:wallet :ui :send :collectible]) - to-address (get-in db [:wallet :ui :send :to-address]) - token-decimal (when token (:decimals token)) - token-id (if token - (:symbol token) - (str (get-in collectible [:id :contract-id :address]) - ":" - (get-in collectible [:id :token-id]))) - network-preferences (if token [] [(get-in collectible [:id :contract-id :chain-id])]) + account-address (get-in db [:wallet :ui :send :send-account-address]) + to-address (or account-address (get-in db [:wallet :ui :send :to-address])) + token-decimal (:decimals token) + token-id (:symbol token) + network-preferences [] gas-rates constants/gas-rate-medium - amount-in (send-utils/amount-in-hex amount (if token token-decimal 0)) + amount-in (send-utils/amount-in-hex amount token-decimal) from-address wallet-address disabled-from-chain-ids [] disabled-to-chain-ids [] from-locked-amount {} - transaction-type (if token - constants/send-type-transfer - constants/send-type-erc-721-transfer) - request-params [transaction-type + request-params [constants/send-type-transfer from-address to-address amount-in @@ -142,46 +134,21 @@ :fx [[:dispatch [:navigate-to :wallet-transaction-progress]]]}))) (defn- transaction-bridge - [{:keys [from-address from-chain-id to-address token-id token-address route data eth-transfer?]}] - (let [{:keys [bridge-name amount-out gas-amount - gas-fees]} route - eip-1559-enabled? (:eip-1559-enabled gas-fees) - {:keys [gas-price max-fee-per-gas-medium - max-priority-fee-per-gas]} gas-fees - transfer-tx (cond-> {:From from-address - :To (or token-address to-address) - :Gas (money/to-hex gas-amount) - :Value (when eth-transfer? amount-out) - :Nonce nil - :Input "" - :Data (or data "0x")} - eip-1559-enabled? (assoc :TxType "0x02" - :MaxFeePerGas - (money/to-hex - (money/->wei - :gwei - max-fee-per-gas-medium)) - :MaxPriorityFeePerGas - (money/to-hex - (money/->wei - :gwei - max-priority-fee-per-gas))) - (not eip-1559-enabled?) (assoc :TxType "0x00" - :GasPrice (money/to-hex - (money/->wei - :gwei - gas-price))))] - [(cond-> {:BridgeName bridge-name - :ChainID from-chain-id} - - (= bridge-name constants/bridge-name-erc-721-transfer) - (assoc :ERC721TransferTx - (assoc transfer-tx - :Recipient to-address - :TokenID token-id)) - - (= bridge-name constants/bridge-name-transfer) - (assoc :TransferTx transfer-tx))])) + [{:keys [from-address to-address route]}] + (let [{:keys [from bridge-name amount-out gas-amount gas-fees]} route + {:keys [gas-price max-fee-per-gas-medium max-priority-fee-per-gas]} gas-fees] + [{:BridgeName bridge-name + :ChainID (:chain-id from) + :TransferTx {:From from-address + :To to-address + :Gas (money/to-hex gas-amount) + :GasPrice (money/to-hex (money/->wei :gwei gas-price)) + :Value amount-out + :Nonce nil + :MaxFeePerGas (money/to-hex (money/->wei :gwei max-fee-per-gas-medium)) + :MaxPriorityFeePerGas (money/to-hex (money/->wei :gwei max-priority-fee-per-gas)) + :Input "" + :Data "0x"}}])) (defn- multi-transaction-command [{:keys [from-address to-address from-asset to-asset amount-out transfer-type] @@ -195,53 +162,27 @@ (rf/reg-event-fx :wallet/send-transaction (fn [{:keys [db]} [sha3-pwd]] - (let [route (get-in db [:wallet :ui :send :route]) - from-address (get-in db [:wallet :current-viewing-account-address]) - token (get-in db [:wallet :ui :send :token]) - collectible (get-in db [:wallet :ui :send :collectible]) - from-chain-id (get-in route [:from :chain-id]) - token-id (if token - (:symbol token) - (get-in collectible [:id :token-id])) - erc20-transfer? (and token (not= token-id "ETH")) - eth-transfer? (and token (not erc20-transfer?)) - token-address (cond collectible - (get-in collectible - [:id :contract-id :address]) - erc20-transfer? - (get-in token [:balances-per-chain from-chain-id :address])) - to-address (get-in db [:wallet :ui :send :to-address]) - data (when erc20-transfer? - (native-module/encode-transfer (address/normalized-hex to-address) - (:amount-out route))) - request-params [(multi-transaction-command - {:from-address from-address - :to-address to-address - :from-asset token-id - :to-asset token-id - :amount-out (if eth-transfer? (:amount-out route) "0x0")}) - (transaction-bridge {:to-address to-address - :from-address from-address - :route route - :from-chain-id from-chain-id - :token-address token-address - :token-id (when collectible - (money/to-hex (js/parseInt token-id))) - :data data - :eth-transfer? eth-transfer?}) - sha3-pwd]] + (let [route (get-in db [:wallet :ui :send :route]) + from-address (get-in db [:wallet :current-viewing-account-address]) + to-address (get-in db [:wallet :ui :send :to-address]) + token (get-in db [:wallet :ui :send :token]) + token-id (:symbol token) + request-params [(multi-transaction-command {:from-address from-address + :to-address to-address + :from-asset token-id + :to-asset token-id + :amount-out (:amount-out route)}) + (transaction-bridge {:to-address to-address + :from-address from-address + :route route}) + sha3-pwd]] {:json-rpc/call [{:method "wallet_createMultiTransaction" :params request-params :on-success (fn [result] (rf/dispatch [:hide-bottom-sheet]) - (rf/dispatch [:wallet/add-authorized-transaction result]) - (rf/dispatch [:wallet/clean-scanned-address]) - (rf/dispatch [:wallet/clean-local-suggestions]) - (rf/dispatch [:wallet/clean-send-address]) - (rf/dispatch [:wallet/select-address-tab nil])) + (rf/dispatch [:wallet/add-authorized-transaction result])) :on-error (fn [error] (log/error "failed to send transaction" {:event :wallet/send-transaction :error error :params request-params}))}]}))) - diff --git a/src/status_im/contexts/wallet/send/input_amount/view.cljs b/src/status_im/contexts/wallet/send/input_amount/view.cljs index d0f2e438f9..5ee3895f91 100644 --- a/src/status_im/contexts/wallet/send/input_amount/view.cljs +++ b/src/status_im/contexts/wallet/send/input_amount/view.cljs @@ -53,6 +53,13 @@ (normalize-input current v) current)) +(defn- find-affordable-networks + [{:keys [balances-per-chain]} input-value] + (->> balances-per-chain + (filter (fn [[_ {:keys [balance]}]] + (>= (js/parseFloat balance) input-value))) + (map first))) + (defn- f-view-internal [{:keys [rate limit]}] (let [bottom (safe-area/get-bottom) @@ -142,9 +149,10 @@ :on-change-text (fn [text] (handle-on-change text))}] [routes/view - {:amount amount - :routes suggested-routes - :networks (:networks token)}] + {:amount amount + :routes suggested-routes + :loading-networks (find-affordable-networks token @input-value) + :networks (:networks token)}] [quo/bottom-actions {:actions :1-action :button-one-label (i18n/label :t/confirm) diff --git a/src/status_im/contexts/wallet/send/routes/view.cljs b/src/status_im/contexts/wallet/send/routes/view.cljs index 5ee5994126..ff8c1f7ea9 100644 --- a/src/status_im/contexts/wallet/send/routes/view.cljs +++ b/src/status_im/contexts/wallet/send/routes/view.cljs @@ -14,11 +14,13 @@ {:amount amount :network from-network :status status}] - [quo/network-link - {:shape :linear - :source from-network - :destination to-network - :container-style style/network-link}] + (if (= status :default) + [quo/network-link + {:shape :linear + :source from-network + :destination to-network + :container-style style/network-link}] + [rn/view {:style {:width 73}}]) [quo/network-bridge {:amount amount :network to-network @@ -26,12 +28,12 @@ :container-style {:right 12}}]]) (defn view - [{:keys [amount routes]}] + [{:keys [amount routes loading-networks]}] (let [loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?]) candidates (:candidates routes)] - (if (and (not loading-suggested-routes?) (not-empty candidates)) + (if (or (and (not-empty loading-networks) loading-suggested-routes?) (not-empty candidates)) [rn/flat-list - {:data candidates + {:data (if loading-suggested-routes? loading-networks candidates) :content-container-style style/routes-container :header [rn/view {:style style/routes-header-container} [quo/section-label @@ -40,15 +42,17 @@ [quo/section-label {:section (i18n/label :t/to-label) :container-style (style/section-label 64)}]] - :render-fn (fn [route] + :render-fn (fn [item] [route-item {:amount amount - :status :default - :from-network (utils/id->network (get-in route [:from :chain-id])) - :to-network (utils/id->network (get-in route - [:to :chain-id]))}])}] + :status (if loading-suggested-routes? :loading :default) + :from-network (if loading-suggested-routes? + (utils/id->network item) + (utils/id->network (get-in item [:from :chain-id]))) + :to-network (if loading-suggested-routes? + (utils/id->network item) + (utils/id->network (get-in item + [:to :chain-id])))}])}] [rn/view {:style style/empty-container} - (if loading-suggested-routes? - [rn/activity-indicator] - (when (not (nil? candidates)) - [quo/text (i18n/label :t/no-routes-found)]))]))) + (when (and (not (nil? candidates)) (not loading-suggested-routes?)) + [quo/text (i18n/label :t/no-routes-found)])]))) diff --git a/src/status_im/contexts/wallet/send/select_address/view.cljs b/src/status_im/contexts/wallet/send/select_address/view.cljs index 6a9e74fb4a..07ae2e684c 100644 --- a/src/status_im/contexts/wallet/send/select_address/view.cljs +++ b/src/status_im/contexts/wallet/send/select_address/view.cljs @@ -38,8 +38,8 @@ (rn/dismiss-keyboard!) (rf/dispatch [:open-modal :scan-address])) :ens-regex constants/regx-ens - :address-regex constants/regx-address :scanned-value (or (when recipient-plain-address? send-address) scanned-address) + :address-regex constants/regx-multichain-address :on-detect-address #(debounce/debounce-and-dispatch [:wallet/validate-address %] 300) @@ -48,8 +48,6 @@ [:wallet/find-ens text contacts chain-id cb] 300)) :on-change-text (fn [text] - (when-not (= scanned-address text) - (rf/dispatch [:wallet/clean-scanned-address])) (when (empty? text) (rf/dispatch [:wallet/clean-local-suggestions])) (reset! input-value text)) diff --git a/src/status_im/subs/wallet/wallet.cljs b/src/status_im/subs/wallet/wallet.cljs index a619a016d7..f9fa40f23c 100644 --- a/src/status_im/subs/wallet/wallet.cljs +++ b/src/status_im/subs/wallet/wallet.cljs @@ -43,6 +43,11 @@ :<- [:wallet/wallet-send] :-> :to-address) +(rf/reg-sub + :wallet/wallet-send-address-prefix + :<- [:wallet/wallet-send] + :-> :address-prefix) + (rf/reg-sub :wallet/wallet-send-route :<- [:wallet/wallet-send]