Swaps: Asset to Pay / Asset to Receive (#21140)

* Select assets to pay/receive

* Fixes

* Fixes

* Updates

* Fixes

* Fixes

* Fixes

* Fixes

* Small test fix
This commit is contained in:
Alexander 2024-09-27 12:31:03 +02:00 committed by GitHub
parent 93f00c442e
commit 7bbd476cf6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 566 additions and 92 deletions

View File

@ -0,0 +1,13 @@
(ns quo.components.list-items.token-info.component-spec
(:require
[quo.components.list-items.token-info.view :as token-info]
[test-helpers.component :as h]))
(h/describe "List Items: Token Info"
(h/test "Token label renders"
(h/render-with-theme-provider [token-info/view
{:token :snt
:label "Status"
:state :default
:customization-color :blue}])
(h/is-truthy (h/get-by-text "Status"))))

View File

@ -0,0 +1,12 @@
(ns quo.components.list-items.token-info.schema)
(def ?schema
[:=>
[:cat
[:map
[:token [:or :string :keyword]]
[:label :string]
[:on-press {:optional true} [:maybe fn?]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:state {:optional true} [:maybe [:enum :default :active :selected :disabled]]]]]
:any])

View File

@ -0,0 +1,42 @@
(ns quo.components.list-items.token-info.style
(:require [quo.foundations.colors :as colors]))
(defn- background-color
[state customization-color theme]
(cond
(= state :pressed) (colors/resolve-color customization-color theme 5)
(= state :active) (colors/resolve-color customization-color theme 10)
(= state :selected) (colors/resolve-color customization-color theme 5)
:else :transparent))
(defn container
[state customization-color theme]
{:flex-direction :row
:justify-content :space-between
:align-items :center
:padding-horizontal 12
:padding-vertical 8
:border-radius 12
:height 56
:opacity (when (= state :disabled) 0.3)
:background-color (background-color state customization-color theme)})
(def info
{:flex-direction :row
:align-items :center
:width "70%"})
(def token-info
{:height 40
:flex 1})
(defn token-description-label
[theme]
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
(def token-image
{:border-width 1
:border-radius 16
:border-color colors/neutral-80-opa-5
:margin-right 8
:background-color colors/neutral-80-opa-5})

View File

@ -0,0 +1,50 @@
(ns quo.components.list-items.token-info.view
(:require
[quo.components.list-items.token-info.schema :as component-schema]
[quo.components.list-items.token-info.style :as style]
[quo.components.markdown.text :as text]
[quo.components.utilities.token.view :as token]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[schema.core :as schema]))
(defn- info
[{:keys [token label]}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style style/info}
(when token
[token/view
{:style style/token-image
:size :size-32
:token token}])
[rn/view {:style style/token-info}
[text/text
{:weight :semi-bold
:number-of-lines 1}
(if-not (empty? label) label "-")]
[text/text
{:weight :medium
:size :paragraph-2
:style (style/token-description-label theme)
:number-of-lines 1}
token]]]))
(defn- view-internal
[{:keys [on-press state customization-color]
:as props
:or {customization-color :blue}}]
(let [theme (quo.theme/use-theme)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
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)
:on-press-in on-press-in
:on-press-out on-press-out
:on-press on-press
:disabled (= state :disabled)
:accessibility-label :token}
[info props]]))
(def view (schema/instrument #'view-internal component-schema/?schema))

View File

@ -97,6 +97,7 @@
quo.components.list-items.quiz-item.view quo.components.list-items.quiz-item.view
quo.components.list-items.saved-address.view quo.components.list-items.saved-address.view
quo.components.list-items.saved-contact-address.view quo.components.list-items.saved-contact-address.view
quo.components.list-items.token-info.view
quo.components.list-items.token-network.view quo.components.list-items.token-network.view
quo.components.list-items.token-value.view quo.components.list-items.token-value.view
quo.components.list-items.user quo.components.list-items.user
@ -348,6 +349,7 @@
(def quiz-item quo.components.list-items.quiz-item.view/view) (def quiz-item quo.components.list-items.quiz-item.view/view)
(def saved-address quo.components.list-items.saved-address.view/view) (def saved-address quo.components.list-items.saved-address.view/view)
(def saved-contact-address quo.components.list-items.saved-contact-address.view/view) (def saved-contact-address quo.components.list-items.saved-contact-address.view/view)
(def token-info quo.components.list-items.token-info.view/view)
(def token-network quo.components.list-items.token-network.view/view) (def token-network quo.components.list-items.token-network.view/view)
(def token-value quo.components.list-items.token-value.view/view) (def token-value quo.components.list-items.token-value.view/view)
(def user quo.components.list-items.user/user) (def user quo.components.list-items.user/user)

View File

@ -63,6 +63,7 @@
quo.components.list-items.quiz-item.component-spec quo.components.list-items.quiz-item.component-spec
quo.components.list-items.saved-address.component-spec quo.components.list-items.saved-address.component-spec
quo.components.list-items.saved-contact-address.component-spec quo.components.list-items.saved-contact-address.component-spec
quo.components.list-items.token-info.component-spec
quo.components.list-items.token-network.component-spec quo.components.list-items.token-network.component-spec
quo.components.list-items.token-value.component-spec quo.components.list-items.token-value.component-spec
quo.components.loaders.skeleton-list.component-spec quo.components.loaders.skeleton-list.component-spec

View File

@ -10,7 +10,7 @@
(defn enabled? [v] (= "1" v)) (defn enabled? [v] (= "1" v))
(goog-define INFURA_TOKEN "") (goog-define INFURA_TOKEN "62f3cee52dbb484198e7339837e263f3")
(goog-define POKT_TOKEN "3ef2018191814b7e1009b8d9") (goog-define POKT_TOKEN "3ef2018191814b7e1009b8d9")
(goog-define STATUS_BUILD_PROXY_USER "") (goog-define STATUS_BUILD_PROXY_USER "")
(goog-define STATUS_BUILD_PROXY_PASSWORD "") (goog-define STATUS_BUILD_PROXY_PASSWORD "")

View File

@ -601,6 +601,8 @@
:terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"}) :terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"})
(def ^:const swap-providers (def ^:const swap-providers
{:paraswap swap-provider-paraswap}) {:paraswap swap-provider-paraswap})
(def ^:const swap-tokens-my :my)
(def ^:const swap-tokens-popular :popular)
(def ^:const token-for-fees-symbol "ETH") (def ^:const token-for-fees-symbol "ETH")

View File

@ -104,8 +104,6 @@
[:profile.settings/get-profile-picture key-uid] [:profile.settings/get-profile-picture key-uid]
(when (ff/enabled? ::ff/wallet.wallet-connect) (when (ff/enabled? ::ff/wallet.wallet-connect)
[:dispatch [:wallet-connect/init]]) [:dispatch [:wallet-connect/init]])
(when (ff/enabled? ::ff/wallet.swap)
[:dispatch [:wallet.tokens/get-token-list]])
(when notifications-enabled? (when notifications-enabled?
[:effects/push-notifications-enable])]}))) [:effects/push-notifications-enable])]})))

View File

@ -60,7 +60,9 @@
(rf/dispatch [:wallet/clean-send-data]) (rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:wallet/start-bridge])) (rf/dispatch [:wallet/start-bridge]))
:swap-action (when (ff/enabled? ::ff/wallet.swap) :swap-action (when (ff/enabled? ::ff/wallet.swap)
#(rf/dispatch [:wallet.swap/start]))}]) (fn []
(rf/dispatch [:wallet.tokens/get-token-list])
(rf/dispatch [:open-modal :screen/wallet.swap-select-asset-to-pay])))}])
[quo/tabs [quo/tabs
{:style style/tabs {:style style/tabs
:size 32 :size 32

View File

@ -34,7 +34,7 @@
(defn view (defn view
[{:keys [content-container-style search-text on-token-press preselected-token-symbol] [{:keys [content-container-style search-text on-token-press preselected-token-symbol]
:or {content-container-style {:padding-horizontal 8}}}] :or {content-container-style {:padding-horizontal 8}}}]
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered search-text]) (let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered {:query search-text}])
currency (rf/sub [:profile/currency]) currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])] currency-symbol (rf/sub [:profile/currency-symbol])]
[gesture/flat-list [gesture/flat-list

View File

@ -73,7 +73,7 @@
[token watch-only? entry-point] [token watch-only? entry-point]
(let [token-symbol (:token token) (let [token-symbol (:token token)
token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered
token-symbol])) {:query token-symbol}]))
fiat-unformatted-value (get-in token [:values :fiat-unformatted-value]) fiat-unformatted-value (get-in token [:values :fiat-unformatted-value])
has-balance? (money/greater-than fiat-unformatted-value (money/bignumber "0")) has-balance? (money/greater-than fiat-unformatted-value (money/bignumber "0"))
selected-account? (rf/sub [:wallet/current-viewing-account-address]) selected-account? (rf/sub [:wallet/current-viewing-account-address])

View File

@ -4,6 +4,7 @@
[quo.foundations.resources :as resources] [quo.foundations.resources :as resources]
[status-im.common.qr-codes.view :as qr-codes] [status-im.common.qr-codes.view :as qr-codes]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.hex :as utils.hex] [utils.hex :as utils.hex]
[utils.money :as money] [utils.money :as money]
[utils.number :as number] [utils.number :as number]
@ -478,3 +479,17 @@
:toAsset to-asset :toAsset to-asset
:fromAmount amount-out :fromAmount amount-out
:type multi-transaction-type}) :type multi-transaction-type})
(defn sort-tokens-by-name
[tokens]
(let [priority #(get constants/token-sort-priority (:symbol %) ##Inf)]
(sort-by (juxt :symbol priority) tokens)))
(defn tokens-with-balance
[tokens networks chain-ids]
(map (fn [token]
(assoc token
:networks (network-utils/network-list token networks)
:available-balance (calculate-total-token-balance token)
:total-balance (calculate-total-token-balance token chain-ids)))
tokens))

View File

@ -189,3 +189,14 @@
nil) nil)
(when $ (when $
(assoc $ :chain-id chain-id)))) (assoc $ :chain-id chain-id))))
(defn sorted-networks-with-details
[networks]
(->> networks
(map
(fn [{:keys [chain-id related-chain-id layer]}]
(assoc (get-network-details chain-id)
:chain-id chain-id
:related-chain-id related-chain-id
:layer layer)))
(sort-by (juxt :layer :short-name))))

View File

@ -15,6 +15,7 @@
[status-im.contexts.wallet.db :as db] [status-im.contexts.wallet.db :as db]
[status-im.contexts.wallet.item-types :as item-types] [status-im.contexts.wallet.item-types :as item-types]
[status-im.contexts.wallet.tokens.events] [status-im.contexts.wallet.tokens.events]
[status-im.feature-flags :as ff]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.collection] [utils.collection]
[utils.ethereum.chain :as chain] [utils.ethereum.chain :as chain]
@ -344,7 +345,9 @@
:Prod :Prod
data-store/rpc->network) data-store/rpc->network)
data)}] data)}]
{:db (assoc-in db [:wallet :networks] network-data)}))) {:fx [(when (ff/enabled? ::ff/wallet.swap)
[:dispatch [:wallet.tokens/get-token-list]])]
:db (assoc-in db [:wallet :networks] network-data)})))
(rf/reg-event-fx (rf/reg-event-fx
:wallet/find-ens :wallet/find-ens

View File

@ -0,0 +1 @@
(ns status-im.contexts.wallet.sheets.select-asset.asset-list.style)

View File

@ -0,0 +1,111 @@
(ns status-im.contexts.wallet.sheets.select-asset.asset-list.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- my-token-component
[{token-symbol :symbol
token-name :name
total-balance :total-balance
bridge-disabled? :bridge-disabled?
:as token}
{:keys [currency currency-symbol on-token-press disable-token-fn preselected-token-symbol]}]
(let [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)]
[rn/view {:style {:padding-horizontal 8}}
[quo/token-network
{:token token-symbol
:label token-name
:token-value (str crypto-formatted " " token-symbol)
:fiat-value fiat-formatted
:networks (seq (:networks token))
:on-press #(on-token-press token)
:state (cond
(or bridge-disabled?
(when disable-token-fn
(disable-token-fn constants/swap-tokens-my token)))
:disabled
(= preselected-token-symbol token-symbol)
:selected)}]]))
(defn- popular-token-component
[{token-symbol :symbol
token-name :name
:as token}
{:keys [on-token-press disable-token-fn]}]
[rn/view {:style {:padding-horizontal 8}}
[quo/token-info
{:token token-symbol
:label token-name
:state (when (and disable-token-fn (disable-token-fn constants/swap-tokens-popular token))
:disabled)
:on-press #(on-token-press token)}]])
(defn- list-component
[{:keys [type] :as token} _ _ render-data]
(if (= type constants/swap-tokens-popular)
[popular-token-component token render-data]
[my-token-component token render-data]))
(defn section-header
[{:keys [title]}]
[quo/divider-label
{:container-style {:padding-horizontal 20
:margin-top 12
:margin-bottom 8}
:tight? false}
title])
(defn view
[{:keys [search-text on-token-press preselected-token-symbol network hide-token-fn
disable-token-fn]}]
(let [my-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query search-text
:chain-ids #{(:chain-id network)}
:hide-token-fn hide-token-fn}])
popular-tokens (rf/sub [:wallet/tokens-filtered
{:query search-text
:chain-ids #{(:chain-id network)}
:hide-token-fn hide-token-fn}])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
sectioned-data (cond-> []
(pos? (count my-tokens))
(conj {:title (i18n/label :t/your-assets-on-network
{:network (:full-name network)})
:data (map #(assoc % :type constants/swap-tokens-my)
my-tokens)})
(pos? (count popular-tokens))
(conj {:title (i18n/label :t/popular-assets-on-network
{:network (:full-name network)})
:data (map #(assoc % :type constants/swap-tokens-popular)
popular-tokens)}))
render-data {:currency currency
:currency-symbol currency-symbol
:on-token-press on-token-press
:preselected-token-symbol preselected-token-symbol
:disable-token-fn disable-token-fn}]
[gesture/section-list
{:data sectioned-data
:sections sectioned-data
:render-data render-data
:render-fn list-component
:sticky-section-headers-enabled false
:render-section-header-fn section-header
:style {:flex 1}
:content-container-style {:padding-bottom 20}
:keyboard-should-persist-taps :handled
:key-fn (fn [{:keys [type title] :as token}]
(str (:symbol token) "-" type "-" title))
:on-scroll-to-index-failed identity}]))

View File

@ -0,0 +1,24 @@
(ns status-im.contexts.wallet.sheets.select-asset.style
(:require
[react-native.navigation :as navigation]
[react-native.platform :as platform]))
(defn container
[window-height]
{:flex 1
:display :flex
:height window-height
:padding-top (when platform/android? (navigation/status-bar-height))})
(def search-input-container
{:padding-horizontal 20
:padding-vertical 8})
(def subheader-container
{:justify-content :flex-start
:align-items :center
:flex-direction :row
:flex-wrap :wrap
:gap 4
:padding-horizontal 20
:margin-bottom 8})

View File

@ -0,0 +1,92 @@
(ns status-im.contexts.wallet.sheets.select-asset.view
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im.contexts.wallet.sheets.select-asset.asset-list.view :as asset-list]
[status-im.contexts.wallet.sheets.select-asset.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- search-input
[search-text on-change-text]
[rn/view {:style style/search-input-container}
[quo/input
{:small? true
:placeholder (i18n/label :t/search-assets)
:icon-name :i/search
:value search-text
:on-change-text on-change-text}]])
(defn- assets-view
[{:keys [search-text on-change-text on-select network hide-token-fn disable-token-fn]}]
[:<>
[search-input search-text on-change-text]
[asset-list/view
{:search-text search-text
:on-token-press (fn [token]
(when on-select
(on-select token))
(rf/dispatch [:hide-bottom-sheet]))
:network network
:hide-token-fn hide-token-fn
:disable-token-fn disable-token-fn}]])
(defn- account-view
[{:keys [emoji name color]}]
(let [theme (quo.theme/use-theme)]
[quo/context-tag
{:theme theme
:type :account
:size 24
:account-name name
:emoji emoji
:customization-color color}]))
(defn- network-view
[{:keys [full-name source]}]
[quo/context-tag
{:size 24
:network-logo source
:network-name full-name
:type :network}])
(defn- subheader-view
[{:keys [account network]}]
[rn/view {:style style/subheader-container}
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/select-asset-on)]
[account-view account]
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/select-asset-via)]
[network-view network]])
(defn view
[{:keys [title on-select hide-token-fn disable-token-fn]}]
(let [account (rf/sub [:wallet/current-viewing-account])
{:keys [network]} (rf/sub [:wallet/swap])
[search-text set-search-text] (rn/use-state "")
window-height (:height (rn/get-window))]
[rn/safe-area-view {:style (style/container window-height)}
[quo/page-nav
{:type :no-title
:icon-name :i/close
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet]))}]
[quo/page-top
{:title title
:title-accessibility-label :title-label}]
[subheader-view
{:account account
:network network}]
[assets-view
{:search-text search-text
:on-change-text #(set-search-text %)
:on-select on-select
:network network
:hide-token-fn hide-token-fn
:disable-token-fn disable-token-fn}]]))

View File

@ -5,6 +5,7 @@
[status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.utils :as send-utils] [status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection] [status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.address :as address] [utils.address :as address]
[utils.debounce :as debounce] [utils.debounce :as debounce]
@ -13,32 +14,58 @@
[utils.number :as number])) [utils.number :as number]))
(rf/reg-event-fx :wallet.swap/start (rf/reg-event-fx :wallet.swap/start
(fn [{:keys [_db]}] (fn [{:keys [db]} [{:keys [asset-to-pay asset-to-receive network]}]]
{:fx [[:dispatch [:open-modal :screen/wallet.swap-select-asset-to-pay]]]})) (let [asset-to-receive' (or asset-to-receive
(swap-utils/select-asset-to-receive
(:wallet db)
(:profile/profile db)
asset-to-pay))
network' (or network
(swap-utils/select-network asset-to-pay))]
{:db (-> db
(assoc-in [:wallet :ui :swap :asset-to-pay] asset-to-pay)
(assoc-in [:wallet :ui :swap :asset-to-receive] asset-to-receive')
(assoc-in [:wallet :ui :swap :network] network'))
:fx (if network'
[[:dispatch
[:navigate-to-within-stack
[:screen/wallet.setup-swap :screen/wallet.swap-select-asset-to-pay]]]
[:dispatch [:wallet.swap/set-default-slippage]]]
[[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (:symbol asset-to-pay)
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet.swap/start
{:asset-to-pay asset-to-pay
:asset-to-receive asset-to-receive'
:network network}]))}])}]]])})))
(rf/reg-event-fx :wallet.swap/select-asset-to-pay (rf/reg-event-fx :wallet.swap/select-asset-to-pay
(fn [{:keys [db]} [{:keys [token network]}]] (fn [{:keys [db]} [{:keys [token]}]]
{:db (-> db {:db (update-in db
(assoc-in [:wallet :ui :swap :asset-to-pay] token) [:wallet :ui :swap]
(assoc-in [:wallet :ui :swap :network] network)) #(-> %
:fx (if network (assoc :asset-to-pay token)
[[:dispatch (dissoc :amount
[:navigate-to-within-stack :amount-hex
[:screen/wallet.setup-swap :screen/wallet.swap-select-asset-to-pay]]] :last-request-uuid
[:dispatch [:wallet.swap/set-default-slippage]]] :swap-proposal
[[:dispatch :error-response
[:show-bottom-sheet :loading-swap-proposal?
{:content (fn [] :approval-transaction-id
[network-selection/view :approved-amount)))}))
{:token-symbol (:symbol token)
:on-select-network (fn [network] (rf/reg-event-fx :wallet.swap/select-asset-to-receive
(rf/dispatch [:hide-bottom-sheet]) (fn [{:keys [db]} [{:keys [token]}]]
(rf/dispatch {:db (update-in db
[:wallet.swap/select-asset-to-pay [:wallet :ui :swap]
{:token token #(-> %
:network network (assoc :asset-to-receive token)
:stack-id (assoc :loading-swap-proposal? true)))}))
:screen/wallet.swap-select-asset-to-pay}]))}])}]]])}))
(rf/reg-event-fx :wallet.swap/set-default-slippage (rf/reg-event-fx :wallet.swap/set-default-slippage
(fn [{:keys [db]}] (fn [{:keys [db]}]
@ -48,10 +75,6 @@
(fn [{:keys [db]} [max-slippage]] (fn [{:keys [db]} [max-slippage]]
{:db (assoc-in db [:wallet :ui :swap :max-slippage] (number/parse-float max-slippage))})) {:db (assoc-in db [:wallet :ui :swap :max-slippage] (number/parse-float max-slippage))}))
(rf/reg-event-fx :wallet.swap/select-asset-to-receive
(fn [{:keys [db]} [{:keys [token]}]]
{:db (assoc-in db [:wallet :ui :swap :asset-to-receive] token)}))
(rf/reg-event-fx :wallet/start-get-swap-proposal (rf/reg-event-fx :wallet/start-get-swap-proposal
(fn [{:keys [db]} [{:keys [amount-in amount-out clean-approval-transaction?]}]] (fn [{:keys [db]} [{:keys [amount-in amount-out clean-approval-transaction?]}]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address]) (let [wallet-address (get-in db [:wallet :current-viewing-account-address])

View File

@ -21,15 +21,7 @@
(defn- assets-view (defn- assets-view
[search-text on-change-text] [search-text on-change-text]
(let [on-token-press (fn [token] (let [on-token-press (fn [token]
(let [token-networks (:networks token) (rf/dispatch [:wallet.swap/start {:asset-to-pay token}]))]
asset-to-receive (rf/sub [:wallet/token-by-symbol "DAI"])]
(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}])
(rf/dispatch [:wallet.swap/select-asset-to-receive
{:token asset-to-receive}])))]
[:<> [:<>
[search-input search-text on-change-text] [search-input search-text on-change-text]
[asset-list/view [asset-list/view

View File

@ -12,6 +12,7 @@
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher] [status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token] [status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings] [status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings]
[status-im.contexts.wallet.swap.setup-swap.style :as style] [status-im.contexts.wallet.swap.setup-swap.style :as style]
[status-im.contexts.wallet.swap.utils :as swap-utils] [status-im.contexts.wallet.swap.utils :as swap-utils]
@ -271,6 +272,31 @@
:customization-color account-color :customization-color account-color
:on-press on-press}}])) :on-press on-press}}]))
(defn- pay-token-bottom-sheet
[]
(let [asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])]
[select-asset/view
{:title (i18n/label :t/select-asset-to-pay)
:on-select (fn [token]
(rf/dispatch [:wallet.swap/select-asset-to-pay {:token token}]))
:hide-token-fn (fn [type {:keys [available-balance]}]
(and (= type constants/swap-tokens-my)
(= available-balance 0)))
:disable-token-fn (fn [_ token]
(= (:symbol token)
(:symbol asset-to-receive)))}]))
(defn- receive-token-bottom-sheet
[]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])]
[select-asset/view
{:title (i18n/label :t/select-asset-to-receive)
:on-select (fn [token]
(rf/dispatch [:wallet.swap/select-asset-to-receive {:token token}]))
:disable-token-fn (fn [_ token]
(= (:symbol token)
(:symbol asset-to-pay)))}]))
(defn view (defn view
[] []
(let [[pay-input-state set-pay-input-state] (rn/use-state controlled-input/init-state) (let [[pay-input-state set-pay-input-state] (rn/use-state controlled-input/init-state)
@ -280,6 +306,7 @@
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?]) loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees]) swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay]) 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]) network (rf/sub [:wallet/swap-network])
pay-input-amount (controlled-input/input-value pay-input-state) pay-input-amount (controlled-input/input-value pay-input-state)
pay-token-decimals (:decimals asset-to-pay) pay-token-decimals (:decimals asset-to-pay)
@ -360,7 +387,7 @@
:clean-approval-transaction? false})))) :clean-approval-transaction? false}))))
[valid-pay-input? loading-swap-proposal? [valid-pay-input? loading-swap-proposal?
pay-input-amount]) pay-input-amount])
on-asset-to-pay-change (fn [] refetch-swap-proposal (fn []
(when valid-pay-input? (when valid-pay-input?
(fetch-swap-proposal (fetch-swap-proposal
{:amount pay-input-amount {:amount pay-input-amount
@ -394,8 +421,11 @@
(controlled-input/set-input-value (controlled-input/set-input-value
input-state input-state
swap-amount))) swap-amount)))
(on-asset-to-pay-change))))) (refetch-swap-proposal)))))
[asset-to-pay]) [asset-to-pay])
(rn/use-effect
refetch-swap-proposal
[asset-to-receive])
[rn/view {:style style/container} [rn/view {:style style/container}
[account-switcher/view [account-switcher/view
{:on-press on-close {:on-press on-close
@ -407,7 +437,7 @@
{:input-state pay-input-state {:input-state pay-input-state
:on-max-press on-max-press :on-max-press on-max-press
:input-focused? pay-input-focused? :input-focused? pay-input-focused?
:on-token-press #(js/alert "Token Pressed") :on-token-press #(rf/dispatch [:show-bottom-sheet {:content pay-token-bottom-sheet}])
:on-approve-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap]) :on-approve-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])
:on-input-focus (fn [] :on-input-focus (fn []
(when platform/android? (rf/dispatch [:dismiss-keyboard])) (when platform/android? (rf/dispatch [:dismiss-keyboard]))
@ -420,7 +450,7 @@
(rf/dispatch [:wallet.swap/flip-assets]))}] (rf/dispatch [:wallet.swap/flip-assets]))}]
[receive-token-input [receive-token-input
{:input-focused? (not pay-input-focused?) {:input-focused? (not pay-input-focused?)
:on-token-press #(js/alert "Token Pressed") :on-token-press #(rf/dispatch [:show-bottom-sheet {:content receive-token-bottom-sheet}])
:on-input-focus #(set-pay-input-focused? false)}]] :on-input-focus #(set-pay-input-focused? false)}]]
[rn/view {:style style/footer-container} [rn/view {:style style/footer-container}
[alert-banner {:pay-input-error? pay-input-error?}] [alert-banner {:pay-input-error? pay-input-error?}]

View File

@ -1,9 +1,10 @@
(ns status-im.contexts.wallet.swap.swap-proposal.view (ns status-im.contexts.wallet.swap.swap-proposal.view
(:require [quo.core :as quo] (:require
[react-native.core :as rn] [quo.core :as quo]
[status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings] [react-native.core :as rn]
[status-im.contexts.wallet.swap.swap-proposal.style :as style] [status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings]
[utils.re-frame :as rf])) [status-im.contexts.wallet.swap.swap-proposal.style :as style]
[utils.re-frame :as rf]))
(defn view (defn view
[] []

View File

@ -1,5 +1,7 @@
(ns status-im.contexts.wallet.swap.utils (ns status-im.contexts.wallet.swap.utils
(:require [status-im.constants :as constants] (:require [status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.i18n :as i18n])) [utils.i18n :as i18n]))
(defn error-message-from-code (defn error-message-from-code
@ -27,3 +29,23 @@
(i18n/label :t/not-enough-assets-to-pay-gas-fees) (i18n/label :t/not-enough-assets-to-pay-gas-fees)
:else :else
(i18n/label :t/something-went-wrong-please-try-again-later))) (i18n/label :t/something-went-wrong-please-try-again-later)))
(defn select-asset-to-receive
[wallet profile asset-to-pay]
(let [wallet-address (get-in wallet [:current-viewing-account-address])
account (-> wallet
:accounts
vals
(utils/get-account-by-address wallet-address))
test-networks-enabled? (get profile :test-networks-enabled?)
networks (-> (get-in wallet
[:wallet :networks (if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))]
(->> (utils/tokens-with-balance (:tokens account) networks nil)
(remove #(= (:symbol %) (:symbol asset-to-pay)))
first)))
(defn select-network
[{:keys [networks]}]
(when (= (count networks) 1)
(first networks)))

View File

@ -10,12 +10,13 @@
(rf/reg-event-fx (rf/reg-event-fx
:wallet.tokens/get-token-list :wallet.tokens/get-token-list
(fn [{:keys [db]}] (fn [{:keys [db]}]
{:db (assoc-in db [:wallet :ui :loading :token-list] true) (when-not (get-in db [:wallet :tokens])
:fx [[:json-rpc/call {:db (assoc-in db [:wallet :ui :loading :token-list] true)
[{:method "wallet_getTokenList" :fx [[:json-rpc/call
:params [] [{:method "wallet_getTokenList"
:on-success [:wallet.tokens/store-token-list] :params []
:on-error [:wallet.tokens/get-token-list-failed]}]]]})) :on-success [:wallet.tokens/store-token-list]
:on-error [:wallet.tokens/get-token-list-failed]}]]]})))
(defn store-token-list (defn store-token-list
[{:keys [db]} [{:keys [data]}]] [{:keys [db]} [{:keys [data]}]]

View File

@ -21,14 +21,7 @@
:wallet/network-details :wallet/network-details
:<- [:wallet/networks-by-mode] :<- [:wallet/networks-by-mode]
(fn [networks] (fn [networks]
(->> networks (network-utils/sorted-networks-with-details networks)))
(map
(fn [{:keys [chain-id related-chain-id layer]}]
(assoc (network-utils/get-network-details chain-id)
:chain-id chain-id
:related-chain-id related-chain-id
:layer layer)))
(sort-by (juxt :layer :short-name)))))
(re-frame/reg-sub (re-frame/reg-sub
:wallet/network-details-by-network-name :wallet/network-details-by-network-name

View File

@ -39,6 +39,11 @@
:<- [:wallet/ui] :<- [:wallet/ui]
:-> :scanned-address) :-> :scanned-address)
(rf/reg-sub
:wallet/tokens
:<- [:wallet]
:-> :tokens)
(rf/reg-sub (rf/reg-sub
:wallet/tokens-loading :wallet/tokens-loading
:<- [:wallet/ui] :<- [:wallet/ui]
@ -418,19 +423,26 @@
:<- [:wallet/current-viewing-account] :<- [:wallet/current-viewing-account]
:<- [:wallet/network-details] :<- [:wallet/network-details]
:<- [:wallet/wallet-send] :<- [:wallet/wallet-send]
(fn [[account networks send-data] [_ query chain-ids]] (fn [[account networks send-data] [_ {:keys [query chain-ids hide-token-fn]}]]
(prn send-data)
(let [tx-type (:tx-type send-data) (let [tx-type (:tx-type send-data)
tokens (map (fn [token] tokens (->> (:tokens account)
(assoc token (map
:bridge-disabled? (and (= tx-type :tx/bridge) (fn [token]
(send-utils/bridge-disabled? (:symbol (assoc token
token))) :bridge-disabled? (and (= tx-type :tx/bridge)
:networks (network-utils/network-list token networks) (send-utils/bridge-disabled? (:symbol
:available-balance (utils/calculate-total-token-balance token) token)))
:total-balance (utils/calculate-total-token-balance token :networks (cond->> (network-utils/network-list token
chain-ids))) networks)
(:tokens account)) chain-ids
(filter #(some #{(:chain-id %)} chain-ids)))
:available-balance (utils/calculate-total-token-balance token)
:total-balance (utils/calculate-total-token-balance
token
chain-ids))))
(filter (fn [{:keys [networks]}]
(pos? (count networks))))
(remove #(when hide-token-fn (hide-token-fn constants/swap-tokens-my %))))
sorted-tokens (utils/sort-tokens tokens)] sorted-tokens (utils/sort-tokens tokens)]
(if query (if query
(let [query-string (string/lower-case query)] (let [query-string (string/lower-case query)]
@ -439,22 +451,38 @@
sorted-tokens)) sorted-tokens))
sorted-tokens)))) sorted-tokens))))
(rf/reg-sub
:wallet/tokens-filtered
:<- [:wallet/tokens]
(fn [{:keys [by-symbol market-values-per-token details-per-token]}
[_ {:keys [query chain-ids hide-token-fn]}]]
(let [tokens (->> by-symbol
(map (fn [token]
(-> token
(assoc :market-values
(get market-values-per-token (:symbol token)))
(assoc :details (get details-per-token (:symbol token))))))
(filter (fn [{:keys [chain-id]}]
(some #{chain-id} chain-ids)))
(remove #(when hide-token-fn
(hide-token-fn constants/swap-tokens-popular %))))
sorted-tokens (utils/sort-tokens-by-name tokens)]
(if query
(let [query-string (string/lower-case query)]
(filter #(or (string/starts-with? (string/lower-case (:name %)) query-string)
(string/starts-with? (string/lower-case (:symbol %)) query-string))
sorted-tokens))
sorted-tokens))))
(rf/reg-sub (rf/reg-sub
:wallet/token-by-symbol :wallet/token-by-symbol
:<- [:wallet/current-viewing-account] :<- [:wallet/current-viewing-account]
:<- [:wallet/network-details] :<- [:wallet/network-details]
(fn [[account networks] [_ token-symbol chain-ids]] (fn [[{:keys [tokens]} networks] [_ token-symbol chain-ids]]
(let [tokens (map (fn [token] (->> (utils/tokens-with-balance tokens networks chain-ids)
(assoc token (filter #(= (string/lower-case (:symbol %))
:networks (network-utils/network-list token networks) (string/lower-case token-symbol)))
:available-balance (utils/calculate-total-token-balance token) first)))
:total-balance (utils/calculate-total-token-balance token
chain-ids)))
(:tokens account))
token (first (filter #(= (string/lower-case (:symbol %))
(string/lower-case token-symbol))
tokens))]
token)))
(rf/reg-sub (rf/reg-sub
:wallet/accounts-without-current-viewing-account :wallet/accounts-without-current-viewing-account

View File

@ -887,7 +887,7 @@
(assoc :currencies currencies) (assoc :currencies currencies)
(assoc-in [:profile/profile :currency] :usd))) (assoc-in [:profile/profile :currency] :usd)))
(is (match? (count (rf/sub [sub-name ""])) 2)) (is (match? (count (rf/sub [sub-name ""])) 2))
(is (match? (count (rf/sub [sub-name "et"])) 1)))) (is (match? (count (rf/sub [sub-name "et"])) 2))))
(h/deftest-sub :wallet/selected-networks->chain-ids (h/deftest-sub :wallet/selected-networks->chain-ids
[sub-name] [sub-name]

View File

@ -1929,6 +1929,7 @@
}, },
"pinned-messages-empty": "Pinned messages will appear here. To pin a message, press and hold it and tap `Pin`", "pinned-messages-empty": "Pinned messages will appear here. To pin a message, press and hold it and tap `Pin`",
"podcasts": "Podcasts", "podcasts": "Podcasts",
"popular-assets-on-network": "Popular assets on {{network}}",
"popular-currencies": "Popular currencies", "popular-currencies": "Popular currencies",
"positive": "Positive", "positive": "Positive",
"powered-by-paraswap": "Powered by Paraswap", "powered-by-paraswap": "Powered by Paraswap",
@ -2187,7 +2188,10 @@
"select-account-first": "Select an account first", "select-account-first": "Select an account first",
"select-another-account": "Select another account", "select-another-account": "Select another account",
"select-asset": "Select asset", "select-asset": "Select asset",
"select-asset-on": "On",
"select-asset-to-pay": "Select asset to pay", "select-asset-to-pay": "Select asset to pay",
"select-asset-to-receive": "Select asset to receive",
"select-asset-via": "via",
"select-chat": "Select chat to start messaging", "select-chat": "Select chat to start messaging",
"select-network": "Select network", "select-network": "Select network",
"select-networks": "Select networks", "select-networks": "Select networks",
@ -2833,6 +2837,7 @@
"you-will-start-from-scratch": "You will start from scratch with a new set of keys", "you-will-start-from-scratch": "You will start from scratch with a new set of keys",
"your-addresses": "Your addresses", "your-addresses": "Your addresses",
"your-answer": "Your answer", "your-answer": "Your answer",
"your-assets-on-network": "Your assets on {{network}}",
"your-card-is-frozen": "Your Keycard is frozen. Reset card access", "your-card-is-frozen": "Your Keycard is frozen. Reset card access",
"your-contact-code": "Granting access authorizes this DApp to retrieve your chat key", "your-contact-code": "Granting access authorizes this DApp to retrieve your chat key",
"your-data-belongs-to-you": "If you lose your seed phrase you lose your data and funds", "your-data-belongs-to-you": "If you lose your seed phrase you lose your data and funds",