diff --git a/src/quo/components/list_items/network_list/view.cljs b/src/quo/components/list_items/network_list/view.cljs index 2c5bd47f75..16628e16f0 100644 --- a/src/quo/components/list_items/network_list/view.cljs +++ b/src/quo/components/list_items/network_list/view.cljs @@ -44,13 +44,14 @@ [:label :string] [:fiat-value :string] [:token-value :string] + [:container-style {:optional true} [:maybe :map]] [:customization-color {:optional true} [:maybe :schema.common/customization-color]] [:state {:optional true} [:enum :pressed :active :disabled :default]] [:on-press {:optional true} [:maybe fn?]]]]] :any]) (defn- view-internal - [{:keys [on-press state customization-color] + [{:keys [on-press container-style state customization-color] :as props :or {customization-color :blue}}] (let [theme (quo.theme/use-theme) @@ -59,7 +60,8 @@ on-press-out (rn/use-callback #(set-pressed false)) internal-state (if pressed? :pressed state)] [rn/pressable - {:style (style/container internal-state customization-color theme) + {:style (merge (style/container internal-state customization-color theme) + container-style) :on-press-in (when-not (= state :disabled) on-press-in) :on-press-out (when-not (= state :disabled) on-press-out) :on-press (when-not (= state :disabled) on-press) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 6b78aa2f25..b508a1e36a 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -505,6 +505,9 @@ (def ^:const optimism-network-name :optimism) (def ^:const arbitrum-network-name :arbitrum) +(def ^:const layer-1-network 1) +(def ^:const layer-2-network 2) + (def ^:const default-network-names [mainnet-network-name optimism-network-name arbitrum-network-name]) (def ^:const default-network-count (count default-network-names)) diff --git a/src/status_im/contexts/wallet/sheets/network_selection/style.cljs b/src/status_im/contexts/wallet/sheets/network_selection/style.cljs new file mode 100644 index 0000000000..feebd702a2 --- /dev/null +++ b/src/status_im/contexts/wallet/sheets/network_selection/style.cljs @@ -0,0 +1,17 @@ +(ns status-im.contexts.wallet.sheets.network-selection.style) + +(defn network-list-container + [mainnet?] + {:margin-horizontal 8 + :padding-vertical (when mainnet? 8)}) + +(def header-container + {:height 62 + :padding-horizontal 20}) + +(def context-tag + {:margin-top 4}) + +(def divider-label + {:padding-top 0 + :padding-bottom 0}) diff --git a/src/status_im/contexts/wallet/sheets/network_selection/view.cljs b/src/status_im/contexts/wallet/sheets/network_selection/view.cljs new file mode 100644 index 0000000000..3b4ed178ac --- /dev/null +++ b/src/status_im/contexts/wallet/sheets/network_selection/view.cljs @@ -0,0 +1,55 @@ +(ns status-im.contexts.wallet.sheets.network-selection.view + (:require [quo.core :as quo] + [quo.foundations.resources :as quo.resources] + [react-native.core :as rn] + [status-im.constants :as constants] + [status-im.contexts.wallet.sheets.network-selection.style :as style] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(defn- network-item + [{:keys [network on-select-network]}] + (let [{:keys [network-name + chain-id]} network + {balance-in-crypto :crypto + balance-in-fiat :fiat} (rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id]) + mainnet? (= network-name constants/mainnet-network-name)] + [quo/network-list + {:label (name network-name) + :network-image (quo.resources/get-network network-name) + :token-value balance-in-crypto + :fiat-value balance-in-fiat + :on-press #(on-select-network network) + :container-style (style/network-list-container mainnet?)}])) + +(defn view + [{:keys [on-select-network]}] + (let [token-symbol (rf/sub [:wallet/swap-asset-to-pay-token-symbol]) + {mainnet-network :mainnet-network + layer-2-networks :layer-2-networks} (rf/sub [:wallet/swap-asset-to-pay-networks]) + render-fn (rn/use-callback (fn [network] + [network-item + {:network network + :on-select-network + on-select-network}]))] + [:<> + [rn/view {:style style/header-container} + [quo/text + {:size :heading-2 + :weight :semi-bold} + (i18n/label :t/select-network)] + [quo/context-tag + {:type :token + :size 24 + :token token-symbol + :container-style style/context-tag}]] + (when mainnet-network + [network-item + {:network mainnet-network + :on-select-network on-select-network}]) + [quo/divider-label {:container-style style/divider-label} + (i18n/label :t/layer-2)] + [rn/flat-list + {:data (vec layer-2-networks) + :render-fn render-fn + :scroll-enabled false}]])) diff --git a/src/status_im/contexts/wallet/swap/events.cljs b/src/status_im/contexts/wallet/swap/events.cljs index 3cdd5e4ea1..c495b90c81 100644 --- a/src/status_im/contexts/wallet/swap/events.cljs +++ b/src/status_im/contexts/wallet/swap/events.cljs @@ -1,18 +1,35 @@ (ns status-im.contexts.wallet.swap.events - (:require [re-frame.core :as rf])) + (:require [re-frame.core :as rf] + [status-im.contexts.wallet.sheets.network-selection.view :as network-selection])) (rf/reg-event-fx :wallet.swap/start (fn [{:keys [_db]}] {:fx [[:dispatch [:open-modal :screen/wallet.swap-select-asset-to-pay]]]})) (rf/reg-event-fx :wallet.swap/select-asset-to-pay - (fn [{:keys [db]} token] - {:db (assoc-in db [:wallet :ui :swap :asset-to-pay] token) - :fx [[:dispatch - [:toasts/upsert - {:id :swap-error - :type :negative - :text "Not implemented yet"}]]]})) + (fn [{:keys [db]} [{:keys [token network]}]] + {:db (-> db + (assoc-in [:wallet :ui :swap :asset-to-pay] token) + (assoc-in [:wallet :ui :swap :network] network)) + :fx [(if network + [:dispatch + [:toasts/upsert + {:id :swap-error + :type :negative + :text "Not implemented yet"}]] + [:dispatch + [:show-bottom-sheet + {:content (fn [] + [network-selection/view + {:token-symbol (:symbol token) + :on-select-network (fn [network] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch + [:wallet.swap/select-asset-to-pay + {:token token + :network network + :stack-id + :screen/wallet.swap-select-asset-to-pay}]))}])}]])]})) (rf/reg-event-fx :wallet.swap/clean-asset-to-pay (fn [{:keys [db]}] diff --git a/src/status_im/contexts/wallet/swap/select_asset_to_pay/view.cljs b/src/status_im/contexts/wallet/swap/select_asset_to_pay/view.cljs index 10879ddbdd..f3c3412311 100644 --- a/src/status_im/contexts/wallet/swap/select_asset_to_pay/view.cljs +++ b/src/status_im/contexts/wallet/swap/select_asset_to_pay/view.cljs @@ -21,9 +21,12 @@ (defn- assets-view [search-text on-change-text] (let [on-token-press (fn [token] - (rf/dispatch [:wallet.swap/select-asset-to-pay - {:token token - :stack-id :screen/wallet.swap-select-asset-to-pay}]))] + (let [token-networks (:networks token)] + (rf/dispatch [:wallet.swap/select-asset-to-pay + {:token token + :network (when (= (count token-networks) 1) + (first token-networks)) + :stack-id :screen/wallet.swap-select-asset-to-pay}])))] [:<> [search-input search-text on-change-text] [asset-list/view diff --git a/src/status_im/subs/root.cljs b/src/status_im/subs/root.cljs index a0c43537ff..44ca7a67bf 100644 --- a/src/status_im/subs/root.cljs +++ b/src/status_im/subs/root.cljs @@ -21,6 +21,7 @@ status-im.subs.wallet.networks status-im.subs.wallet.saved-addresses status-im.subs.wallet.send + status-im.subs.wallet.swap status-im.subs.wallet.wallet status-im.subs.wallet.wallet-connect)) diff --git a/src/status_im/subs/wallet/swap.cljs b/src/status_im/subs/wallet/swap.cljs new file mode 100644 index 0000000000..93523c33d1 --- /dev/null +++ b/src/status_im/subs/wallet/swap.cljs @@ -0,0 +1,54 @@ +(ns status-im.subs.wallet.swap + (:require [re-frame.core :as rf] + [status-im.constants :as constants] + [status-im.contexts.wallet.common.utils :as utils] + [utils.money :as money])) + +(rf/reg-sub + :wallet/swap + :<- [:wallet/ui] + :-> :swap) + +(rf/reg-sub + :wallet/swap-asset-to-pay + :<- [:wallet/swap] + :-> :asset-to-pay) + +(rf/reg-sub + :wallet/swap-asset-to-pay-token-symbol + :<- [:wallet/swap-asset-to-pay] + :-> :symbol) + +(rf/reg-sub + :wallet/swap-asset-to-pay-networks + :<- [:wallet/swap-asset-to-pay] + (fn [token] + (let [{token-networks :networks} token + grouped-networks (group-by :layer + token-networks) + mainnet-network (first (get grouped-networks constants/layer-1-network)) + layer-2-networks (get grouped-networks constants/layer-2-network)] + {:mainnet-network mainnet-network + :layer-2-networks layer-2-networks}))) + +(rf/reg-sub + :wallet/swap-asset-to-pay-network-balance + :<- [:wallet/swap-asset-to-pay] + :<- [:profile/currency] + :<- [:profile/currency-symbol] + :<- [:wallet/swap-asset-to-pay-token-symbol] + (fn [[token currency currency-symbol token-symbol] [_ chain-id]] + (let [{:keys [balances-per-chain + decimals]} token + balance-for-chain (get balances-per-chain chain-id) + total-balance (money/token->unit (:raw-balance balance-for-chain) decimals) + fiat-value (utils/calculate-token-fiat-value + {:currency currency + :balance total-balance + :token token}) + crypto-formatted (utils/get-standard-crypto-format token total-balance) + fiat-formatted (utils/get-standard-fiat-format crypto-formatted + currency-symbol + fiat-value)] + {:crypto (str crypto-formatted " " token-symbol) + :fiat fiat-formatted}))) diff --git a/src/status_im/subs/wallet/swap_test.cljs b/src/status_im/subs/wallet/swap_test.cljs new file mode 100644 index 0000000000..3e46034ebd --- /dev/null +++ b/src/status_im/subs/wallet/swap_test.cljs @@ -0,0 +1,107 @@ +(ns status-im.subs.wallet.swap-test + (:require + [cljs.test :refer [is testing]] + [re-frame.db :as rf-db] + [status-im.subs.root] + [status-im.subs.wallet.collectibles] + [test-helpers.unit :as h] + [utils.re-frame :as rf])) + +(def networks + {:mainnet-network + {:full-name "Mainnet" + :network-name :mainnet + :chain-id 1 + :related-chain-id 5 + :layer 1} + :layer-2-networks + [{:full-name "Optimism" + :network-name :optimism + :chain-id 10 + :related-chain-id 420 + :layer 2} + {:full-name "Arbitrum" + :network-name :arbitrum + :chain-id 42161 + :related-chain-id 421613 + :layer 2}]}) + +(def swap-data + {:asset-to-pay + {:description "Status Network Token (SNT)" + :decimals 18 + :symbol "SNT" + :name "Status" + :total-balance 1 + :balances-per-chain + {1 + {:raw-balance 1000000000000000000 + :balance "1" + :chain-id 1} + 10 + {:raw-balance 0 + :balance "0" + :chain-id 10} + 42161 + {:raw-balance 0 + :balance "0" + :chain-id 42161}} + :networks (concat [(networks :mainnet-network)] (networks :layer-2-networks)) + :chain-id 0 + :market-values-per-currency + {:usd + {:change-24hour -0.00109422754667007 + :change-pct-day -5.57352274163899 + :change-pct-24hour -4.177805426737527 + :high-day 0.0271858672171352 + :market-cap 170783296.1155821 + :has-error false + :change-pct-hour -0.0160462113709363 + :low-day 0.02473516779550377 + :price 0.0251}} + :asset-website-url "https://status.im/" + :available-balance 1 + :token-list-id "" + :built-on "ETH" + :verified true} + :network nil}) + +(h/deftest-sub :wallet/swap + [sub-name] + (testing "Return the wallet/ui/swap node" + (swap! rf-db/app-db assoc-in + [:wallet :ui :swap] + swap-data) + (is (match? swap-data (rf/sub [sub-name]))))) + +(h/deftest-sub :wallet/swap-asset-to-pay + [sub-name] + (testing "Return swap asset-to-pay" + (swap! rf-db/app-db assoc-in + [:wallet :ui :swap] + swap-data) + (is (match? (swap-data :asset-to-pay) (rf/sub [sub-name]))))) + +(h/deftest-sub :wallet/swap-asset-to-pay-token-symbol + [sub-name] + (testing "Return asset-to-pay token symbol" + (swap! rf-db/app-db assoc-in + [:wallet :ui :swap] + swap-data) + (is (match? "SNT" (rf/sub [sub-name]))))) + +(h/deftest-sub :wallet/swap-asset-to-pay-networks + [sub-name] + (testing "Return the available networks for the swap asset-to-pay" + (swap! rf-db/app-db assoc-in + [:wallet :ui :swap] + swap-data) + (is (match? networks (rf/sub [sub-name]))))) + +(h/deftest-sub :wallet/swap-asset-to-pay-network-balance + [sub-name] + (testing "Return swap asset-to-pay" + (swap! rf-db/app-db assoc-in + [:wallet :ui :swap] + swap-data) + (is (match? {:crypto "1 SNT" :fiat "$0.03"} (rf/sub [sub-name 1]))))) diff --git a/translations/en.json b/translations/en.json index 77d142bdbf..c816b405d8 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2627,6 +2627,7 @@ "derivation-path-header": "Derivation path", "derivation-path-desc": "Derivation paths are the routes your Status Wallet uses to generate addresses from your private key.", "select-networks": "Select networks", + "select-network": "Select network", "generating-keypair": "Generating key pair...", "keypair-name": "Key pair name", "keypair-name-description": "Name key pair for your own personal reference",