feat(wallet): simplify send and bridge flow ux (#21595)

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2024-12-23 12:00:17 -03:00 committed by GitHub
parent a85ca5bd3d
commit 23d3a2db2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 918 additions and 1410 deletions

View File

@ -1,6 +1,5 @@
(ns status-im.contexts.wallet.bridge.bridge-to.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[quo.theme]
@ -16,27 +15,34 @@
(defn- bridge-token-component
[]
(fn [{:keys [chain-id network-name]} {:keys [supported-networks] :as token}]
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])
balance (utils/calculate-total-token-balance token [chain-id])
crypto-value (utils/get-standard-crypto-format token balance prices-per-token)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
fiat-value)
token-available-on-network? (network-utils/token-available-on-network? supported-networks
chain-id)]
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])
selected-network (rf/sub [:wallet/send-network])
{selected-network-chain-id :chain-id} selected-network
balance (utils/calculate-total-token-balance token [chain-id])
crypto-value (utils/get-standard-crypto-format token
balance
prices-per-token)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
fiat-value)
token-available-on-network? (network-utils/token-available-on-network?
supported-networks
chain-id)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network (:network-name network))
:token-value (str crypto-value " " (:symbol token))
:fiat-value fiat-formatted
:state (if token-available-on-network? :default :disabled)
:state (if (and token-available-on-network? (not= chain-id selected-network-chain-id))
:default
:disabled)
:on-press #(rf/dispatch [:wallet/select-bridge-network
{:network-chain-id chain-id
:stack-id :screen/wallet.bridge-to}])}])))
@ -55,8 +61,7 @@
(assoc account-token
:networks (:networks token)
:supported-networks (:supported-networks token)))
bridge-to-title (i18n/label :t/bridge-to
{:name (string/upper-case (str token-symbol))})]
bridge-to-title (i18n/label :t/select-network-to-receive)]
(hot-reload/use-safe-unmount #(rf/dispatch [:wallet/clean-bridge-to-selection]))

View File

@ -1,9 +1,7 @@
(ns status-im.contexts.wallet.bridge.flow-config)
(def steps
[{:screen-id :screen/wallet.select-from
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
{:screen-id :screen/wallet.bridge-select-asset
[{:screen-id :screen/wallet.bridge-select-asset
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :token])))}
{:screen-id :screen/wallet.bridge-to
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :bridge-to-chain-id])))}

View File

@ -21,5 +21,4 @@
:stack-id :screen/wallet.bridge-input-amount}]))
:on-navigate-back (fn []
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
(rf/dispatch [:wallet/clean-disabled-from-networks])
(rf/dispatch [:wallet/clean-send-amount]))}]])

View File

@ -77,24 +77,31 @@
(defn token-value-drawer
[token watch-only? entry-point]
(let [token-symbol (:token token)
token-data (rf/sub [:wallet/token-by-symbol token-symbol])
token-data (rf/sub [:wallet/token-by-symbol-from-first-available-account-with-balance
token-symbol])
selected-account (rf/sub [:wallet/current-viewing-account-address])
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
account-owns-token? (rf/sub [:wallet/current-account-owns-token token-symbol])
network-details (rf/sub [:wallet/network-details])
receive-token-symbol (if (= token-symbol "SNT") "ETH" "SNT")
token-owned? (if selected-account account-owns-token? (seq token-owners))
asset-to-receive (rf/sub [:wallet/token-by-symbol receive-token-symbol])
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?
:asset-to-receive asset-to-receive}
unique-owner? (= (count token-owners) 1)
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
:has-balance? (-> (get-in token [:values :fiat-unformatted-value])
money/above-zero?))
(not selected-account)
(and (not selected-account) unique-owner?)
(assoc :token-symbol token-symbol
:token token-data
:stack-id :wallet-stack)
(and (not selected-account) (not unique-owner?))
(assoc :token-symbol token-symbol
:stack-id :wallet-stack))]
[quo/action-drawer
@ -108,9 +115,13 @@
(action-send params entry-point))
(action-receive selected-account)
(when token-owned?
(action-swap params))
(action-swap (assoc params
:asset-to-receive
asset-to-receive)))
(when token-owned?
(action-bridge (assoc params
:network-details
network-details
:bridge-disabled?
(send-utils/bridge-disabled? token-symbol))))]))]]))
@ -120,7 +131,11 @@
(cond-> item
(or (not watch-only?) (ff/enabled? ::ff/wallet.long-press-watch-only-asset))
(assoc :on-long-press
#(rf/dispatch
[:show-bottom-sheet
{:content (fn [] [token-value-drawer item watch-only? entry-point])
:selected-item (fn [] [quo/token-value item])}])))])
(fn []
(when (= entry-point :wallet-stack)
(rf/dispatch [:wallet/close-account-page])
(rf/dispatch [:wallet/clean-current-viewing-account]))
(rf/dispatch
[:show-bottom-sheet
{:content (fn [] [token-value-drawer item watch-only? entry-point])
:selected-item (fn [] [quo/token-value item])}]))))])

View File

@ -546,14 +546,20 @@
(let [priority #(get constants/token-sort-priority (:symbol %) ##Inf)]
(sort-by (juxt :symbol priority) tokens)))
(defn token-with-balance
([token networks]
(token-with-balance token networks nil))
([token networks chain-ids]
(assoc token
:networks (network-utils/network-list-with-positive-balance token networks)
:supported-networks (network-utils/network-list token networks)
:available-balance (calculate-total-token-balance token)
:total-balance (calculate-total-token-balance token chain-ids))))
(defn tokens-with-balance
[tokens networks chain-ids]
(map (fn [token]
(assoc token
:networks (network-utils/network-list-with-positive-balance token networks)
:supported-networks (network-utils/network-list token networks)
:available-balance (calculate-total-token-balance token)
:total-balance (calculate-total-token-balance token chain-ids)))
(token-with-balance token networks chain-ids))
tokens))
(defn estimated-time-format

View File

@ -1,23 +1,11 @@
(ns status-im.contexts.wallet.common.wizard.events
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]
(:require [status-im.contexts.wallet.common.wizard.utils :as wizard-utils]
[utils.re-frame :as rf]))
(defn- wizard-find-next-screen
[db flow-id current-screen]
(let [flow-config (case flow-id
:wallet-send-flow wallet-send-flow/steps
:wallet-bridge-flow wallet-bridge-flow/steps
nil)]
(first (filter (fn [{:keys [skip-step? screen-id]}]
(and (not= screen-id current-screen)
(not (and (fn? skip-step?) (skip-step? db)))))
flow-config))))
(rf/reg-event-fx
:wallet/wizard-navigate-forward
(fn [{:keys [db]} [{:keys [current-screen flow-id start-flow?]}]]
(let [next-screen (wizard-find-next-screen db flow-id current-screen)]
(let [next-screen (wizard-utils/wizard-find-next-screen db flow-id current-screen)]
{:fx [[:dispatch
(if start-flow?
[:open-modal (:screen-id next-screen)]

View File

@ -0,0 +1,14 @@
(ns status-im.contexts.wallet.common.wizard.utils
(:require [status-im.contexts.wallet.bridge.flow-config :as wallet-bridge-flow]
[status-im.contexts.wallet.send.flow-config :as wallet-send-flow]))
(defn wizard-find-next-screen
[db flow-id current-screen]
(let [flow-config (case flow-id
:wallet-send-flow wallet-send-flow/steps
:wallet-bridge-flow wallet-bridge-flow/steps
nil)]
(first (filter (fn [{:keys [skip-step? screen-id]}]
(and (not= screen-id current-screen)
(not (and (fn? skip-step?) (skip-step? db)))))
flow-config))))

View File

@ -10,11 +10,13 @@
[status-im.contexts.settings.wallet.effects]
[status-im.contexts.settings.wallet.events]
[status-im.contexts.wallet.common.activity-tab.events]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.external-links :as external-links]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.db :as db]
[status-im.contexts.wallet.item-types :as item-types]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[status-im.contexts.wallet.tokens.events]
[status-im.feature-flags :as ff]
[taoensso.timbre :as log]
@ -355,19 +357,79 @@
(rf/reg-event-fx :wallet/get-keypairs get-keypairs)
(rf/reg-event-fx :wallet/bridge-select-token
(fn [{:keys [db]} [{:keys [token token-symbol stack-id start-flow?]}]]
(let [missing-recipient? (-> db :wallet :ui :send :to-address nil?)
to-address (-> db :wallet :current-viewing-account-address)]
(fn [{:keys [db]}
[{:keys [token token-symbol stack-id network owners network-details start-flow?] :as params}]]
(let [{:keys [wallet]} db
unique-owner (when (= (count owners) 1)
(first owners))
token (if (and unique-owner (nil? token))
(let [token (utils/get-token-from-account db
token-symbol
unique-owner)]
(utils/token-with-balance token network-details))
token)
missing-recipient? (-> db :wallet :ui :send :to-address nil?)
to-address (or unique-owner
(-> db :wallet :current-viewing-account-address)
(utils/get-default-account (-> db :wallet :accounts)))
view-id (:view-id db)
root-screen? (or (= view-id :wallet-stack) (nil? view-id))
multi-account-balance? (-> (utils/get-accounts-with-token-balance (:accounts wallet)
token)
(count)
(> 1))
account-not-defined? (and (not unique-owner) multi-account-balance?)
networks-with-balance (when (and token (:balances-per-chain token))
(filter #(not= (:balance %) "0")
(vals (:balances-per-chain token))))
balance-in-only-one-network? (when networks-with-balance (= (count networks-with-balance) 1))
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
network-details (-> (get-in db
[:wallet :networks
(if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))
network (if balance-in-only-one-network?
(first (filter #(= (:chain-id %)
(:chain-id (first networks-with-balance)))
network-details))
network)]
{:db (cond-> db
:always (assoc-in [:wallet :ui :send :tx-type] :tx/bridge)
token (assoc-in [:wallet :ui :send :token] token)
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
network (assoc-in [:wallet :ui :send :network] network)
missing-recipient? (assoc-in [:wallet :ui :send :to-address] to-address))
:fx [[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]})))
:fx (cond
;; If the token has a balance in more than one account and this was dispatched from the
;; general wallet screen, open the account selection screen.
(and account-not-defined? root-screen?)
[[:dispatch [:open-modal :screen/wallet.select-from]]]
;; If the token has a balance in only one account (or this was dispatched from the
;; account screen) and the network is already set, navigate forward in the bridge flow.
(some? network)
[[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]
;; If we know which account to bridge the token from but the network is not set yet,
;; show the network selection drawer.
:else
[[:dispatch [:wallet/switch-current-viewing-account to-address]]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:title (i18n/label :t/select-network-to-bridge-from)
:token-symbol (or token-symbol (:symbol token))
:source :bridge
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/bridge-select-token
(assoc params :network network)]))}])}]]])})))
(rf/reg-event-fx :wallet/start-bridge
(fn [{:keys [db]}]

View File

@ -7,6 +7,7 @@
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[taoensso.timbre :as log]
[utils.money :as utils.money]
[utils.number]
@ -41,64 +42,42 @@
(fn [{:keys [db]} [suggested-routes-data enough-assets?]]
(if-not enough-assets?
{:db (update-in db [:wallet :ui :send] add-not-enough-assets-data (:best suggested-routes-data))}
(let [chosen-route (:best suggested-routes-data)
(let [chosen-route (:best suggested-routes-data)
{:keys [token collectible token-display-name
receiver-networks
receiver-network-values
sender-network-values tx-type
disabled-from-chain-ids
from-locked-amounts]} (get-in db [:wallet :ui :send])
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
token-networks (:supported-networks token)
token-networks-ids (when token-networks
(map #(:chain-id %) token-networks))
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? false})
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? true})
sender-possible-chain-ids (map :chain-id sender-network-values)
sender-network-values (if routes-available?
(send-utils/network-amounts
{:network-values
(if (= tx-type :tx/bridge)
from-network-amounts-by-chain
(send-utils/add-zero-values-to-network-values
from-network-amounts-by-chain
sender-possible-chain-ids))
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:from-locked-amounts from-locked-amounts
:tx-type tx-type
:receiver? false})
(send-utils/reset-loading-network-amounts-to-zero
sender-network-values))
receiver-network-values (if routes-available?
(send-utils/network-amounts
{:network-values to-network-amounts-by-chain
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true})
(cond->
(send-utils/reset-loading-network-amounts-to-zero
receiver-network-values)
(not= tx-type :tx/bridge)
send-utils/safe-add-type-edit))
network-links (when routes-available?
(send-utils/network-links chosen-route
sender-network-values
receiver-network-values))]
sender-network-values tx-type]} (get-in db [:wallet :ui :send])
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? false})
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? true})
sender-possible-chain-ids (map :chain-id sender-network-values)
sender-network-values (if routes-available?
(send-utils/network-amounts
(if (= tx-type :tx/bridge)
from-network-amounts-by-chain
(send-utils/add-zero-values-to-network-values
from-network-amounts-by-chain
sender-possible-chain-ids)))
(send-utils/reset-loading-network-amounts-to-zero
sender-network-values))
receiver-network-values (if routes-available?
(send-utils/network-amounts
to-network-amounts-by-chain)
(send-utils/reset-loading-network-amounts-to-zero
receiver-network-values))
network-links (when routes-available?
(send-utils/network-links chosen-route
sender-network-values
receiver-network-values))]
{:db (update-in db
[:wallet :ui :send]
assoc
@ -137,34 +116,19 @@
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui :send] dissoc :amount)}))
(rf/reg-event-fx :wallet/clean-disabled-from-networks
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui :send] dissoc :disabled-from-chain-ids)}))
(rf/reg-event-fx :wallet/clean-from-locked-amounts
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui :send] dissoc :from-locked-amounts)}))
(rf/reg-event-fx
:wallet/select-send-address
(fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]]
(let [[prefix to-address] (utils/split-prefix-and-address address)
testnet-enabled? (get-in db [:profile/profile :test-networks-enabled?])
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
collectible-tx? (send-utils/tx-type-collectible?
(-> db :wallet :ui :send :tx-type))
collectible (when collectible-tx?
(-> db :wallet :ui :send :collectible))
one-collectible? (when collectible-tx?
(= (collectible.utils/collectible-balance collectible) 1))]
(let [[_ to-address] (utils/split-prefix-and-address address)
collectible-tx? (send-utils/tx-type-collectible?
(-> db :wallet :ui :send :tx-type))
collectible (when collectible-tx?
(-> db :wallet :ui :send :collectible))
one-collectible? (when collectible-tx?
(= (collectible.utils/collectible-balance collectible) 1))]
{: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)
(assoc-in [:wallet :ui :send :receiver-preferred-networks] receiver-networks)
(assoc-in [:wallet :ui :send :receiver-networks] receiver-networks))
(assoc-in [:wallet :ui :send :to-address] to-address))
:fx [(when (and collectible-tx? one-collectible?)
[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]])
[:dispatch
@ -182,32 +146,47 @@
(rf/reg-event-fx
:wallet/set-token-to-send
(fn [{:keys [db]} [{:keys [token-symbol token stack-id start-flow? owners]} entry-point]]
(fn [{:keys [db]}
[{:keys [token-symbol token network stack-id start-flow? owners] :as params} entry-point]]
;; `token` is a map extracted from the sender, but in the wallet home page we don't know the
;; sender yet, so we only provide the `token-symbol`, later in
;; `:wallet/select-from-account` the `token` key will be set.
(let [{:keys [networks]} token
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
token-networks-ids (map :chain-id networks)
unsupported-token? (not-any? (set receiver-networks) token-networks-ids)
unique-owner (when (= (count owners) 1)
(first owners))
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
token-data (or token
(when (and token-symbol unique-owner)
(some #(when (= (:symbol %) token-symbol) %)
unique-owner-tokens)))
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
network-details (-> (get-in db
[:wallet :networks (if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))]
(let [{:keys [wallet]} db
unique-owner (when (= (count owners) 1)
(first owners))
unique-owner-tokens (get-in db [:wallet :accounts unique-owner :tokens])
token-data (or token
(when (and token-symbol unique-owner)
(some #(when (= (:symbol %) token-symbol) %)
unique-owner-tokens)))
view-id (:view-id db)
root-screen? (or (= view-id :wallet-stack) (nil? view-id))
multi-account-balance? (-> (utils/get-accounts-with-token-balance (:accounts wallet)
token)
(count)
(> 1))
account-not-defined? (and (not unique-owner) multi-account-balance?)
networks-with-balance (when (and token-data (:balances-per-chain token))
(filter #(not= (:balance %) "0")
(vals (:balances-per-chain token))))
balance-in-only-one-network? (when networks-with-balance (= (count networks-with-balance) 1))
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
network-details (-> (get-in db
[:wallet :networks
(if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))
network (if balance-in-only-one-network?
(first (filter #(= (:chain-id %)
(:chain-id (first networks-with-balance)))
network-details))
network)]
(println multi-account-balance? "43247329479847392")
(when (or token-data token-symbol)
{:db (cond-> db
:always (update-in [:wallet :ui :send]
network (update-in [:wallet :ui :send]
#(-> %
(dissoc :collectible :tx-type)
(assoc :token-not-supported-in-receiver-networks?
unsupported-token?)))
(assoc :network network)))
token-symbol (assoc-in [:wallet :ui :send :token-symbol] token-symbol)
token-data (update-in [:wallet :ui :send]
#(assoc %
@ -216,20 +195,45 @@
(network-utils/network-list
token-data
network-details))
:token-display-name (:symbol token-data)))
:token-display-name (:symbol token-data)
:token-symbol (:symbol token-data)))
unique-owner (assoc-in [:wallet :current-viewing-account-address] unique-owner)
entry-point (assoc-in [:wallet :ui :send :entry-point] entry-point))
:fx [[:dispatch [:wallet/stop-and-clean-suggested-routes]]
[:dispatch
;; ^:flush-dom allows us to make sure the re-frame DB state is always synced
;; before the navigation occurs, so the new screen is always rendered with
;; the DB state set by this event. By adding the metadata we are omitting
;; a 1-frame blink when the screen is mounted.
^:flush-dom
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}))))
:fx (cond
;; If the token has a balance in more than one account and this was dispatched from
;; the general wallet screen, open the account selection screen.
(and account-not-defined? root-screen?)
[[:dispatch [:open-modal :screen/wallet.select-from]]]
;; If the token has a balance in only one account (or this was dispatched from the
;; account screen) and the network is already set, stop and clean suggested routes,
;; then navigate forward in the send flow.
(some? network)
[[:dispatch [:wallet/stop-and-clean-suggested-routes]]
[:dispatch
;; ^:flush-dom allows us to make sure the re-frame DB state is always synced
;; before the navigation occurs, so the new screen is always rendered with
;; the DB state set by this event. By adding the metadata we are omitting
;; a 1-frame blink when the screen is mounted.
^:flush-dom
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]
;; If we don't know the network but need to set it, show the network selection drawer.
:else
[[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (or token-symbol (:symbol token-data))
:source :send
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/set-token-to-send
(assoc params :network network)]))}])}]]])}))))
(rf/reg-event-fx
:wallet/edit-token-to-send
@ -246,8 +250,7 @@
(assoc-in [:wallet :ui :send :token-not-supported-in-receiver-networks?]
token-not-supported-in-receiver-networks?))
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:wallet/stop-and-clean-suggested-routes]]
[:dispatch [:wallet/clean-from-locked-amounts]]]})))
[:dispatch [:wallet/stop-and-clean-suggested-routes]]]})))
(rf/reg-event-fx :wallet/clean-selected-token
(fn [{:keys [db]}]
@ -367,32 +370,15 @@
(fn [{:keys [db]}]
(let [keys-to-remove [:to-values-by-chain :network-links :sender-network-values :route
:receiver-network-values :suggested-routes :from-values-by-chain
:loading-suggested-routes? :amount]]
:loading-suggested-routes? :amount :enough-assets?]]
{:db (update-in db [:wallet :ui :send] #(apply dissoc % keys-to-remove))})))
(rf/reg-event-fx :wallet/disable-from-networks
(fn [{:keys [db]} [chain-ids]]
{:db (assoc-in db [:wallet :ui :send :disabled-from-chain-ids] chain-ids)}))
(rf/reg-event-fx :wallet/lock-from-amount
(fn [{:keys [db]} [chain-id amount]]
{:db (assoc-in db [:wallet :ui :send :from-locked-amounts chain-id] amount)}))
(rf/reg-event-fx :wallet/unlock-from-amount
(fn [{:keys [db]} [chain-id]]
{:db (update-in db [:wallet :ui :send :from-locked-amounts] dissoc chain-id)}))
(rf/reg-event-fx :wallet/reset-network-amounts-to-zero
(fn [{:keys [db]}]
(let [sender-network-values (get-in db [:wallet :ui :send :sender-network-values])
receiver-network-values (get-in db [:wallet :ui :send :receiver-network-values])
disabled-from-chain-ids (get-in db [:wallet :ui :send :disabled-from-chain-ids])
sender-network-values (send-utils/reset-network-amounts-to-zero
{:network-amounts sender-network-values
:disabled-chain-ids disabled-from-chain-ids})
receiver-network-values (send-utils/reset-network-amounts-to-zero
{:network-amounts receiver-network-values
:disabled-chain-ids []})]
sender-network-values (send-utils/reset-network-amounts-to-zero sender-network-values)
receiver-network-values (send-utils/reset-network-amounts-to-zero receiver-network-values)]
{:db (-> db
(assoc-in [:wallet :ui :send :sender-network-values] sender-network-values)
(assoc-in [:wallet :ui :send :receiver-network-values] receiver-network-values)
@ -404,89 +390,62 @@
(rf/reg-event-fx :wallet/start-get-suggested-routes
(fn [{:keys [db]} [{:keys [amount amount-out updated-token] :as args :or {amount-out "0"}}]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
{:keys [token tx-type collectible to-address
receiver-networks disabled-from-chain-ids
from-locked-amounts bridge-to-chain-id]
network bridge-to-chain-id]
:or {token updated-token}} (get-in db [:wallet :ui :send])
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db [:wallet :networks (if test-networks-enabled? :test :prod)])
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
to-token-id ""
gas-rates constants/gas-rate-medium
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
amount-in (to-hex amount)
amount-out (to-hex amount-out)
from-address wallet-address
disabled-to-chain-ids (if (= tx-type :tx/bridge)
(filter #(not= % bridge-to-chain-id) network-chain-ids)
(filter (fn [chain-id]
(not (some #(= chain-id %)
receiver-networks)))
network-chain-ids))
from-locked-amount (update-vals from-locked-amounts to-hex)
send-type (case tx-type
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
:tx/bridge constants/send-type-bridge
constants/send-type-transfer)
balances-per-chain (when token (:balances-per-chain token))
sender-token-available-networks-for-suggested-routes
(when token
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain balances-per-chain
:disabled-chain-ids disabled-from-chain-ids
:only-with-balance? true}))
receiver-token-available-networks-for-suggested-routes
(when token
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain balances-per-chain
:disabled-chain-ids disabled-from-chain-ids
:only-with-balance? false}))
token-networks-ids (when token (map #(:chain-id %) (:supported-networks token)))
sender-network-values (when sender-token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
{:valid-networks
(if (= tx-type :tx/bridge)
(remove #(= bridge-to-chain-id %)
sender-token-available-networks-for-suggested-routes)
sender-token-available-networks-for-suggested-routes)
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? false}))
receiver-network-values (when receiver-token-available-networks-for-suggested-routes
(send-utils/loading-network-amounts
{:valid-networks
(if (= tx-type :tx/bridge)
(filter
#(= bridge-to-chain-id %)
receiver-token-available-networks-for-suggested-routes)
receiver-token-available-networks-for-suggested-routes)
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true}))
request-uuid (str (random-uuid))
params [{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
:amountIn amount-in
:amountOut amount-out
:tokenID token-id
:toTokenID to-token-id
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount from-locked-amount
:username (:username args)
:publicKey (:publicKey args)
:packID (:packID args)}]]
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks (get-in db
[:wallet :networks
(if test-networks-enabled? :test :prod)])
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
to-token-id ""
gas-rates constants/gas-rate-medium
to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0)))
amount-in (to-hex amount)
amount-out (to-hex amount-out)
from-address wallet-address
network-chain-id (if collectible
(get-in collectible [:id :contract-id :chain-id])
(:chain-id network))
disabled-from-chain-ids (filter #(not= % network-chain-id) network-chain-ids)
disabled-to-chain-ids (filter #(not= %
(if (= tx-type :tx/bridge)
bridge-to-chain-id
network-chain-id))
network-chain-ids)
send-type (case tx-type
:tx/collectible-erc-721 constants/send-type-erc-721-transfer
:tx/collectible-erc-1155 constants/send-type-erc-1155-transfer
:tx/bridge constants/send-type-bridge
constants/send-type-transfer)
sender-network-values (when (= tx-type :tx/bridge)
(send-utils/loading-network-amounts
{:networks [network-chain-id]
:values {network-chain-id amount}
:receiver? false}))
receiver-network-values (when (= tx-type :tx/bridge)
(send-utils/loading-network-amounts
{:networks [bridge-to-chain-id]
:receiver? true}))
request-uuid (str (random-uuid))
params [{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
:amountIn amount-in
:amountOut amount-out
:tokenID token-id
:toTokenID to-token-id
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount {}
:username (:username args)
:publicKey (:publicKey args)
:packID (:packID args)}]]
(when (and to-address from-address amount-in token-id)
{:db (update-in db
[:wallet :ui :send]
@ -623,7 +582,6 @@
[:dispatch [:wallet/clean-scanned-address]]
[:dispatch [:wallet/clean-local-suggestions]]
[:dispatch [:wallet/clean-send-address]]
[:dispatch [:wallet/clean-disabled-from-networks]]
[:dispatch [:wallet/select-address-tab nil]]]}))
(rf/reg-event-fx :wallet/end-transaction-flow
@ -712,34 +670,60 @@
(rf/reg-event-fx
:wallet/select-from-account
(fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]]
(fn [{db :db} [{:keys [address stack-id network-details network start-flow?] :as params}]]
(let [{:keys [token-symbol
tx-type]} (-> db :wallet :ui :send)
token (when token-symbol
;; When this flow has started in the wallet home page, we know the
;; token or collectible to send, but we don't know from which
;; account, so we extract the token data from the picked account.
(let [token (utils/get-token-from-account db token-symbol address)]
(assoc token
:networks (network-utils/network-list-with-positive-balance
token
network-details)
:supported-networks (network-utils/network-list token
network-details)
:total-balance (utils/calculate-total-token-balance token))))
bridge-tx? (= tx-type :tx/bridge)
flow-id (if bridge-tx?
:wallet-bridge-flow
:wallet-send-flow)]
tx-type]} (-> db :wallet :ui :send)
token (when token-symbol
;; When this flow has started in the wallet home page, we
;; know the token or collectible to send, but we don't know
;; from which account, so we extract the token data from the
;; picked account.
(let [token (utils/get-token-from-account db
token-symbol
address)]
(utils/token-with-balance token network-details)))
bridge-tx? (= tx-type :tx/bridge)
flow-id (if bridge-tx?
:wallet-bridge-flow
:wallet-send-flow)
networks-with-balance (when (and token (:balances-per-chain token))
(filter #(not= (:balance %) "0")
(vals (:balances-per-chain token))))
balance-in-only-one-network? (when networks-with-balance (= (count networks-with-balance) 1))
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
network-details (-> (get-in db
[:wallet :networks
(if test-networks-enabled? :test :prod)])
(network-utils/sorted-networks-with-details))
network (if balance-in-only-one-network?
(first (filter #(= (:chain-id %)
(:chain-id (first networks-with-balance)))
network-details))
network)]
{:db (cond-> db
network (assoc-in [:wallet :ui :send :network] network)
token-symbol (assoc-in [:wallet :ui :send :token] token)
bridge-tx? (assoc-in [:wallet :ui :send :to-address] address))
:fx [[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id flow-id}]]]})))
:fx (if (some? network)
[[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? (and start-flow? (not balance-in-only-one-network?))
:flow-id flow-id}]]]
[[:dispatch [:dismiss-modal :screen/wallet.select-from]]
[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:show-bottom-sheet
{:content (fn []
[network-selection/view
{:token-symbol (or token-symbol (:symbol token))
:source :send
:on-select-network (fn [network]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch
[:wallet/select-from-account
(assoc params :network network)]))}])}]]])})))
(rf/reg-event-fx
:wallet/transaction-confirmation-navigate-back

View File

@ -1,9 +1,9 @@
(ns status-im.contexts.wallet.send.events-test
(:require
[cljs.test :refer-macros [is testing]]
[matcher-combinators.matchers :as m]
matcher-combinators.test
[re-frame.db :as rf-db]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
status-im.contexts.wallet.send.events
[status-im.contexts.wallet.send.utils :as send-utils]
[test-helpers.unit :as h]
@ -40,19 +40,17 @@
receiver-networks [421614 11155420]]
(testing "can be called with :token"
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
:token-not-supported-in-receiver-networks? false}}}}
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
:token-symbol token-symbol}}}}
_ (reset! rf-db/app-db initial-db)
result (dispatch [event-id {:token token}])]
(is (match? expected-db (:db result)))))
(testing "can be called with :token-symbol"
(testing "can be called with :token"
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
expected-db {:wallet {:ui {:send {:token-symbol token-symbol
:token-not-supported-in-receiver-networks? true}}}}
_ (reset! rf-db/app-db initial-db)
result (dispatch [event-id {:token-symbol token-symbol}])]
(is (match? expected-db (:db result))))))
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
expected-db {:wallet {:ui {:send {:token-symbol token-symbol}}}}
_ (reset! rf-db/app-db initial-db)
result (dispatch [event-id {:token-symbol token-symbol}])]
(is (match? expected-db (:db result)))))
(testing "shouldn't have changes if called without :token or :token-symbol")
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks}}}}
expected-db nil
@ -62,16 +60,16 @@
(testing "should clean :collectible set"
(let [initial-db {:wallet {:ui {:send {:receiver-networks receiver-networks
:collectible "some-collectible"}}}}
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
:token-not-supported-in-receiver-networks? false}}}}
expected-db {:wallet {:ui {:send {:token-display-name token-symbol}}}}
_ (reset! rf-db/app-db initial-db)
result (dispatch [event-id {:token token}])]
result (dispatch [event-id
{:token token
:network "network"}])]
(is (match? expected-db (:db result)))
(is (match? nil (get-in result [:db :wallet :ui :send :collectible])))))
(testing "should set :token-not-supported-in-receiver-networks?"
(let [initial-db {:wallet {:ui {:send {:receiver-networks []}}}}
expected-db {:wallet {:ui {:send {:token-display-name token-symbol
:token-not-supported-in-receiver-networks? true}}}}
expected-db {:wallet {:ui {:send {:token-display-name token-symbol}}}}
_ (reset! rf-db/app-db initial-db)
result (dispatch [event-id {:token token}])]
(is (match? expected-db (:db result)))))))
@ -211,47 +209,6 @@
:other-props :value}}}})
(is (match-strict? expected-db (:db (dispatch [event-id]))))))
(h/deftest-event :wallet/clean-disabled-from-networks
[event-id dispatch]
(let [expected-db {:wallet {:ui {:send {:other-props :value}}}}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:disabled-from-chain-ids [:optimism]
:other-props :value}}}})
(is (match-strict? expected-db (:db (dispatch [event-id]))))))
(h/deftest-event :wallet/clean-from-locked-amounts
[event-id dispatch]
(let [expected-db {:wallet {:ui {:send {:other-props :value}}}}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:from-locked-amounts "value"
:other-props :value}}}})
(is (match-strict? expected-db (:db (dispatch [event-id]))))))
(h/deftest-event :wallet/disable-from-networks
[event-id dispatch]
(let [expected-db {:wallet {:ui {:send {:disabled-from-chain-ids [:optimism]
:other-props :value}}}}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:other-props :value}}}})
(is (match? expected-db (:db (dispatch [event-id [:optimism]]))))))
(h/deftest-event :wallet/unlock-from-amount
[event-id dispatch]
(let [expected-db {:wallet {:ui {:send {:other-props :value
:from-locked-amounts {}}}}}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:other-props :value
:from-locked-amounts {:chain-id [1 10]}}}}})
(is (match? expected-db (:db (dispatch [event-id :chain-id]))))))
(h/deftest-event :wallet/lock-from-amount
[event-id dispatch]
(let [expected-db {:wallet {:ui {:send {:other-props :value
:from-locked-amounts {:amount 10}}}}}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:other-props :value}}}})
(is (match? expected-db (:db (dispatch [event-id :amount 10]))))))
(h/deftest-event :wallet/clean-selected-token
[event-id dispatch]
(let [expected-db {:wallet {:ui {:send {:other-props :value}}}}]
@ -303,13 +260,8 @@
(let [sender-network-values [{:chain-id 1 :total-amount (money/bignumber "100") :type :default}
{:chain-id 10 :total-amount (money/bignumber "200") :type :default}]
receiver-network-values [{:chain-id 1 :total-amount (money/bignumber "100") :type :loading}]
disabled-from-chain-ids [:ethereum]
sender-network-zero (send-utils/reset-network-amounts-to-zero
{:network-amounts sender-network-values
:disabled-chain-ids disabled-from-chain-ids})
receiver-network-zero (send-utils/reset-network-amounts-to-zero
{:network-amounts receiver-network-values
:disabled-chain-ids []})]
sender-network-zero (send-utils/reset-network-amounts-to-zero sender-network-values)
receiver-network-zero (send-utils/reset-network-amounts-to-zero receiver-network-values)]
(testing "if sender-network-value and receiver-network-value are not empty"
(let [expected-db {:wallet {:ui {:send {:other-props :value
:sender-network-values sender-network-zero
@ -347,7 +299,6 @@
(h/deftest-event :wallet/select-send-address
[event-id dispatch]
(let [address "eth:arb1:0x01"
prefix "eth:arb1:"
to-address "0x01"
recipient {:type :saved-address
:label "0x01...23f"}
@ -355,27 +306,20 @@
start-flow? false
tx-type :tx/collectible-erc-721]
(testing "testing when collectible balance is more than 1"
(let [collectible (collectible-with-balance 2)
testnet-enabled? false
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
expected-result {:db {:wallet {:ui {:send {:other-props :value
:recipient recipient
:to-address to-address
:address-prefix prefix
:receiver-preferred-networks
receiver-networks
:receiver-networks receiver-networks
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? false}}
:fx [nil
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}]
(let [collectible (collectible-with-balance 2)
testnet-enabled? false
expected-result {:db {:wallet {:ui {:send {:other-props :value
:recipient recipient
:to-address to-address
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? false}}
:fx [nil
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:other-props :value
:tx-type tx-type
@ -388,27 +332,20 @@
:stack-id stack-id
:start-flow? start-flow?}])))))
(testing "testing when collectible balance is 1"
(let [collectible (collectible-with-balance 1)
testnet-enabled? false
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
expected-result {:db {:wallet {:ui {:send {:other-props :value
:recipient recipient
:to-address to-address
:address-prefix prefix
:receiver-preferred-networks
receiver-networks
:receiver-networks receiver-networks
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? false}}
:fx [[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}]
(let [collectible (collectible-with-balance 1)
testnet-enabled? false
expected-result {:db {:wallet {:ui {:send {:other-props :value
:recipient recipient
:to-address to-address
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? false}}
:fx [[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:other-props :value
:tx-type tx-type
@ -484,7 +421,6 @@
:networks #{{:chain-id 421614}
{:chain-id 11155420}
{:chain-id 11155111}}}
token-networks (:networks token)
routes-available? (pos? (count chosen-route))
sender-network-values [1 10]
receiver-network-values [1 10]
@ -506,37 +442,18 @@
to-network-values-for-ui (send-utils/network-values-for-ui to-network-amounts-by-chain)
tx-type :tx/collectible-erc-1155
from-network-values-for-ui (send-utils/network-values-for-ui from-network-amounts-by-chain)
disabled-from-chain-ids [:421614]
token-networks-ids (when token-networks (mapv #(:chain-id %) token-networks))
sender-possible-chain-ids (mapv :chain-id sender-network-values)
receiver-network-values (if routes-available?
(send-utils/network-amounts
{:network-values to-network-values-for-ui
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true})
(cond->
(send-utils/reset-loading-network-amounts-to-zero
receiver-network-values)
(not= tx-type :tx/bridge)
send-utils/safe-add-type-edit))
(send-utils/network-amounts to-network-values-for-ui)
(send-utils/reset-loading-network-amounts-to-zero
receiver-network-values))
sender-network-values (if routes-available?
(send-utils/network-amounts
{:network-values
(if (= tx-type :tx/bridge)
(if (= tx-type :tx/bridge)
from-network-values-for-ui
(send-utils/add-zero-values-to-network-values
from-network-values-for-ui
(send-utils/add-zero-values-to-network-values
from-network-values-for-ui
sender-possible-chain-ids))
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:from-locked-amounts {}
:tx-type tx-type
:receiver? false})
sender-possible-chain-ids)))
(send-utils/reset-loading-network-amounts-to-zero
sender-network-values))
expected-db {:wallet {:ui {:send
@ -544,7 +461,6 @@
:suggested-routes suggested-routes-data
:route chosen-route
:token token
:disabled-from-chain-ids disabled-from-chain-ids
:suggested-routes-call-timestamp timestamp
:collectible (collectible-with-balance 1)
:token-display-name token-symbol
@ -559,8 +475,7 @@
chosen-route
sender-network-values
receiver-network-values))
:loading-suggested-routes? false
:from-locked-amounts {}}}}}]
:loading-suggested-routes? false}}}}]
(reset! rf-db/app-db
{:wallet {:ui {:send {:other-props :value
:suggested-routes-call-timestamp timestamp
@ -570,17 +485,29 @@
:receiver-networks receiver-networks
:receiver-network-values [1 10]
:sender-network-values [1 10]
:tx-type tx-type
:disabled-from-chain-ids disabled-from-chain-ids
:from-locked-amounts {}}}}})
:tx-type tx-type}}}})
(is (match? expected-db (:db (dispatch [event-id suggested-routes timestamp]))))))
(h/deftest-event :wallet/select-from-account
[event-id dispatch]
(let [stack-id :screen/stack
start-flow? false
address "0x01"]
address "0x01"
network {:chain-id 1}]
(testing "when tx-type is :tx/bridge and token-symbol is nil"
(let [tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address
:tx-type tx-type}}}}
:fx [[:dispatch [:dismiss-modal :screen/wallet.select-from]]
[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch [:show-bottom-sheet {:content (m/pred fn?)}]]]}]
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type}}}})
(is (match? expected-result
(dispatch [event-id
{:address address
:stack-id stack-id
:start-flow? start-flow?}])))))
(testing "when tx-type is :tx/bridge, network is selected and token-symbol is nil"
(let [flow-id :wallet-bridge-flow
tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address
@ -596,6 +523,7 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?}])))))
(testing "when tx-type is not :tx/bridge and token-symbol is nil"
(let [flow-id :wallet-send-flow
@ -612,42 +540,22 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?}])))))
(testing "when tx-type is :tx/bridge and token-symbol is not nil"
(let [flow-id :wallet-bridge-flow
tx-type :tx/bridge
tokens [{:symbol "ETH"
:chain-id 1
:balances-per-chain {1 {:raw-balance (money/bignumber 100)}
10 {:raw-balance (money/bignumber 200)}
42161 {:raw-balance (money/bignumber 300)}}
:decimals 2}]
network-details #{{:chain-id 1}
{:chain-id 10}
{:chain-id 42161}}
expected-result {:db {:wallet {:ui {:send {:to-address address
:tx-type tx-type
:token-symbol "ETH"
:token (assoc (first tokens)
:networks #{nil}
:total-balance
(money/bignumber 6))}}
:accounts {address {:tokens tokens}}}}
:fx [[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id flow-id}]]]}]
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type
:token-symbol "ETH"}}
:accounts {address {:tokens tokens}}}})
(let [tx-type :tx/bridge
expected-result {:db {:wallet {:ui {:send {:to-address address
:tx-type tx-type}}}}
:fx [[:dispatch [:dismiss-modal :screen/wallet.select-from]]
[:dispatch [:wallet/switch-current-viewing-account address]]
[:dispatch [:show-bottom-sheet {:content (m/pred fn?)}]]]}]
(reset! rf-db/app-db {:wallet {:ui {:send {:tx-type tx-type
:token-symbol "ETH"}}}})
(is (match? expected-result
(dispatch [event-id
{:address address
:stack-id stack-id
:start-flow? start-flow?
:netork-details network-details}])))))
{:address address
:stack-id stack-id
:start-flow? start-flow?}])))))
(testing "when tx-type is not :tx/bridge and token-symbol is not nil"
(let [flow-id :wallet-send-flow
tx-type :tx/collectible-erc-721
@ -680,5 +588,6 @@
(dispatch [event-id
{:address address
:stack-id stack-id
:network network
:start-flow? start-flow?
:netork-details network-details}])))))))

View File

@ -14,9 +14,7 @@
(-> db :wallet :ui :send :token some?))
(def steps
[{:screen-id :screen/wallet.select-from
:skip-step? (fn [db] (some? (get-in db [:wallet :current-viewing-account-address])))}
{:screen-id :screen/wallet.select-address
[{:screen-id :screen/wallet.select-address
:skip-step? (fn [db] (some? (get-in db [:wallet :ui :send :recipient])))}
{:screen-id :screen/wallet.select-asset
:skip-step? (fn [db] (or (token-selected? db) (collectible-selected? db)))}

View File

@ -1,13 +1,14 @@
(ns status-im.contexts.wallet.send.from.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.send.from.style :as style]
[status-im.setup.hot-reload :as hot-reload]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- on-account-press
@ -15,27 +16,29 @@
(rf/dispatch [:wallet/select-from-account
{:address address
:network-details network-details
:stack-id :screen/wallet.select-from}]))
(defn- on-close
[]
(rf/dispatch [:wallet/clean-current-viewing-account]))
:stack-id :screen/wallet.select-from
:start-flow? true}]))
(defn- render-fn
[item _ _ {:keys [network-details]}]
(let [transformed-address (rf/sub [:wallet/account-address (:address item)
(:network-preferences-names item)])]
(let [has-balance (money/above-zero? (string/replace-first (:asset-pay-balance item) "<" ""))]
[quo/account-item
{:on-press #(on-account-press (:address item) network-details)
{:type (if has-balance :tag :default)
:on-press #(on-account-press (:address item) network-details)
:state (if has-balance :default :disabled)
:token-props {:symbol (:asset-pay-symbol item)
:value (:asset-pay-balance item)}
:account-props (assoc item
:address transformed-address
:address (:formatted-address item)
:full-address? true)}]))
(defn view
[]
(let [accounts (rf/sub [:wallet/accounts-with-current-asset])
(let [token-symbol (rf/sub [:wallet/send-token-symbol])
token (rf/sub [:wallet/token-by-symbol-from-first-available-account-with-balance
token-symbol])
accounts (rf/sub [:wallet/accounts-with-balances token])
network-details (rf/sub [:wallet/network-details])]
(hot-reload/use-safe-unmount on-close)
[floating-button-page/view
{:footer-container-padding 0
:header [account-switcher/view

View File

@ -12,103 +12,92 @@
(set! debounce/debounce-and-dispatch #())
(def sub-mocks
{:profile/profile {:currency :usd}
:wallet/selected-network-details [{:source 525
:short-name "eth"
:network-name :mainnet
:chain-id 1
:related-chain-id 5}]
:wallet/current-viewing-account-address "0x1"
:wallet/current-viewing-account {:path "m/44'/60'/0'/0/1"
:emoji "💎"
:key-uid "0x2f5ea39"
:address "0x1"
:wallet false
:name "Account One"
:type :generated
:watch-only? false
:chat false
:test-preferred-chain-ids #{5 420 421613}
:color :purple
:hidden false
:prod-preferred-chain-ids #{1 10 42161}
:network-preferences-names #{:mainnet :arbitrum
:optimism}
:position 1
:clock 1698945829328
:created-at 1698928839000
:operable :fully
:mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064"
:removed false}
:wallet/current-viewing-account-color :purple
:wallet/wallet-send-enough-assets? true
:wallet/wallet-send-token {:symbol "ETH"
:networks [{:source 879
:short-name "eth"
:network-name :mainnet
:abbreviated-name
"Eth."
:full-name "Mainnet"
:chain-id 1
:related-chain-id 1
:layer 1}]
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}
:total-balance 100
:available-balance 100}
:wallet/wallet-send-loading-suggested-routes? false
:wallet/wallet-send-route [{:from {:chainid 1
:native-currency-symbol "ETH"}
:to {:chain-id 1
:native-currency-symbol "ETH"}
:gas-amount "23487"
:gas-fees {:base-fee "32.325296406"
:max-priority-fee-per-gas "0.011000001"
:eip1559-enabled true}}]
:wallet/wallet-send-suggested-routes nil
:wallet/wallet-send-receiver-networks [1]
:view-id :screen/wallet.send-input-amount
:wallet/wallet-send-to-address "0x04371e2d9d66b82f056bc128064"
:profile/currency-symbol "$"
:profile/currency :usd
:wallet/token-by-symbol {:symbol "ETH"
:total-balance 100
:available-balance 100
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}}
:wallet/wallet-send-disabled-from-chain-ids []
:wallet/wallet-send-from-locked-amounts {}
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}
:wallet/wallet-send-sender-network-values nil
:wallet/wallet-send-receiver-network-values nil
:wallet/wallet-send-network-links nil
:wallet/wallet-send-receiver-preferred-networks [1]
:wallet/wallet-send-enabled-networks [{:source 879
:short-name "eth"
:network-name :mainnet
:abbreviated-name "Eth."
:full-name "Mainnet"
:chain-id 1
:related-chain-id 1
:layer 1}]
:wallet/wallet-send-enabled-from-chain-ids [1]
:wallet/send-amount nil
:wallet/wallet-send-tx-type :tx/send
:wallet/wallet-send-fee-fiat-formatted "$5,00"
:wallet/sending-collectible? false
:wallet/selected-keypair-keycard? false
:wallet/send-total-amount-formatted "250 ETH"
:wallet/total-amount (money/bignumber "250")
:wallet/prices-per-token {:ETH {:usd 10}}
:wallet/bridge-to-network-details nil
:wallet/send-amount-fixed ""
:wallet/send-display-token-decimals 5})
{:profile/profile {:currency :usd}
:wallet/selected-network-details [{:source 525
:short-name "eth"
:network-name :mainnet
:chain-id 1
:related-chain-id 5}]
:wallet/current-viewing-account-address "0x1"
:wallet/current-viewing-account {:path "m/44'/60'/0'/0/1"
:emoji "💎"
:key-uid "0x2f5ea39"
:address "0x1"
:wallet false
:name "Account One"
:type :generated
:watch-only? false
:chat false
:test-preferred-chain-ids #{5 420 421613}
:color :purple
:hidden false
:prod-preferred-chain-ids #{1 10 42161}
:network-preferences-names #{:mainnet :arbitrum
:optimism}
:position 1
:clock 1698945829328
:created-at 1698928839000
:operable :fully
:mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064"
:removed false}
:wallet/current-viewing-account-color :purple
:wallet/wallet-send-enough-assets? true
:wallet/wallet-send-token {:symbol "ETH"
:networks [{:source 879
:short-name "eth"
:network-name :mainnet
:abbreviated-name
"Eth."
:full-name "Mainnet"
:chain-id 1
:related-chain-id 1
:layer 1}]
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}
:total-balance 100
:available-balance 100}
:wallet/wallet-send-loading-suggested-routes? false
:wallet/wallet-send-route [{:from {:chainid 1
:native-currency-symbol "ETH"}
:to {:chain-id 1
:native-currency-symbol "ETH"}
:gas-amount "23487"
:gas-fees {:base-fee "32.325296406"
:max-priority-fee-per-gas "0.011000001"
:eip1559-enabled true}}]
:wallet/wallet-send-suggested-routes nil
:wallet/wallet-send-receiver-networks [1]
:view-id :screen/wallet.send-input-amount
:wallet/wallet-send-to-address "0x04371e2d9d66b82f056bc128064"
:profile/currency-symbol "$"
:profile/currency :usd
:wallet/token-by-symbol {:symbol "ETH"
:total-balance 100
:available-balance 100
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}}
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}
:wallet/wallet-send-sender-network-values nil
:wallet/wallet-send-receiver-network-values nil
:wallet/wallet-send-network-links nil
:wallet/send-amount nil
:wallet/wallet-send-tx-type :tx/send
:wallet/wallet-send-fee-fiat-formatted "$5,00"
:wallet/sending-collectible? false
:wallet/selected-keypair-keycard? false
:wallet/send-total-amount-formatted "250 ETH"
:wallet/total-amount (money/bignumber "250")
:wallet/prices-per-token {:ETH {:usd 10}}
:wallet/bridge-to-network-details nil
:wallet/send-amount-fixed ""
:wallet/send-display-token-decimals 5
:wallet/send-network {:chain-id 1}})
(h/describe "Send > input amount screen"
(h/setup-restorable-re-frame)

View File

@ -1,6 +1,7 @@
(ns status-im.contexts.wallet.send.input-amount.estimated-fees
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[status-im.common.not-implemented :as not-implemented]
@ -30,38 +31,40 @@
(let [amount (rf/sub [:wallet/send-total-amount-formatted])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
{:keys [full-name]} (rf/sub [:wallet/bridge-to-network-details])]
[quo/data-item
(cond-> {:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/recipient-gets)
:subtitle amount}
(= tx-type :tx/bridge)
(assoc
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:on-press show-bonder-fee-info))]))
(when (= tx-type :tx/bridge)
[quo/data-item
{:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:subtitle amount
:on-press show-bonder-fee-info}])))
(defn view
[{:keys [loading-routes? fees]}]
[rn/view {:style style/estimated-fees-container}
(when (ff/enabled? ::ff/wallet.advanced-sending)
[rn/view {:style style/estimated-fees-content-container}
[quo/button
{:icon-only? true
:type :outline
:size 32
:inner-style {:opacity 1}
:accessibility-label :advanced-button
:disabled? loading-routes?
:on-press not-implemented/alert}
:i/advanced]])
[quo/data-item
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/fees)
:subtitle fees}]
[received-amount {:loading-routes? loading-routes?}]])
[{:keys [not-enough-asset? loading-routes? fees]}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style style/estimated-fees-container}
(when (ff/enabled? ::ff/wallet.advanced-sending)
[rn/view {:style style/estimated-fees-content-container}
[quo/button
{:icon-only? true
:type :outline
:size 32
:inner-style {:opacity 1}
:accessibility-label :advanced-button
:disabled? loading-routes?
:on-press not-implemented/alert}
:i/advanced]])
[quo/data-item
(cond-> {:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/max-fees)
:subtitle fees}
not-enough-asset? (assoc :subtitle-color
(colors/theme-colors colors/danger-50
colors/danger-60
theme)))]
[received-amount {:loading-routes? loading-routes?}]]))

View File

@ -25,10 +25,9 @@
:height 40})
(def fees-data-item
{:flex 1
:height 40
:margin-horizontal 16
:background-color :transparent})
{:flex 1
:height 40
:background-color :transparent})
(def amount-data-item
{:flex 1

View File

@ -2,7 +2,6 @@
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
@ -15,29 +14,18 @@
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.contexts.wallet.send.routes.view :as routes]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
[status-im.setup.hot-reload :as hot-reload]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.re-frame :as rf]))
(defn- every-network-value-is-zero?
[sender-network-values]
(every? (fn [{:keys [total-amount]}]
(and
total-amount
(money/bignumber? total-amount)
(money/equal-to total-amount
(money/bignumber "0"))))
sender-network-values))
(defn select-asset-bottom-sheet
[clear-input!]
(let [{preselected-token-symbol :symbol} (rf/sub [:wallet/wallet-send-token])]
[:<> ;; Need to be a `:<>` to keep `asset-list` scrollable.
[quo/drawer-top
{:title (i18n/label :t/select-asset)
{:title (i18n/label :t/select-token)
:container-style {:padding-bottom 8}}]
[asset-list/view
{:content-container-style {:padding-horizontal 8
@ -47,37 +35,6 @@
(rf/dispatch [:wallet/edit-token-to-send token])
(clear-input!))}]]))
(defn- token-not-available
[token-symbol receiver-networks token-networks]
(let [theme (quo.theme/use-theme)
add-token-networks (fn []
(let [chain-ids (concat receiver-networks
(mapv #(:chain-id %) token-networks))]
(rf/dispatch [:wallet/update-receiver-networks chain-ids])))]
[rn/view {:style (style/token-not-available-container theme)}
[rn/view
[quo/icon :i/alert
{:size 16
:color (colors/resolve-color :danger theme)}]]
[rn/view {:style style/token-not-available-content-container}
[quo/text
{:style (style/token-not-available-text theme)
:size :paragraph-2}
(i18n/label :t/token-not-available-on-receiver-networks {:token-symbol token-symbol})]
[quo/button
{:size 24
:customization-color (colors/resolve-color :danger theme)
:on-press add-token-networks}
(i18n/label :t/add-networks-token-can-be-sent-to {:token-symbol token-symbol})]]]))
(defn- show-unpreferred-networks-alert
[on-confirm]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[unpreferred-networks-alert/view
{:on-confirm on-confirm}])}]))
(defn- no-routes-found
[]
[rn/view {:style style/no-routes-found-container}
@ -118,8 +75,7 @@
(rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])))
(defn- insufficient-asset-amount?
[{:keys [token-symbol owned-eth-token input-state no-routes-found? limit-exceeded?
sender-network-values enough-assets?]}]
[{:keys [token-symbol owned-eth-token input-state limit-exceeded? enough-assets?]}]
(let [eth-selected? (= token-symbol (string/upper-case constants/mainnet-short-name))
zero-owned-eth? (money/equal-to (:total-balance owned-eth-token) 0)
input-at-max-owned-amount? (money/equal-to
@ -128,9 +84,9 @@
exceeded-input? (if eth-selected?
input-at-max-owned-amount?
zero-owned-eth?)]
(and (or no-routes-found? limit-exceeded?)
(seq sender-network-values)
(or exceeded-input? (not enough-assets?)))))
(or exceeded-input?
limit-exceeded?
(false? enough-assets?))))
(defn view
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
@ -142,122 +98,103 @@
button-one-props :button-one-props
current-screen-id :current-screen-id
initial-crypto-currency? :initial-crypto-currency?
enabled-from-chain-ids :enabled-from-chain-ids
from-enabled-networks :from-enabled-networks
:or {initial-crypto-currency? true}}]
(let [view-id (rf/sub [:view-id])
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
[crypto-currency?
set-crypto-currency] (rn/use-state initial-crypto-currency?)
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
token-networks :networks
:as token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
(let [view-id (rf/sub [:view-id])
{fiat-currency :currency} (rf/sub [:profile/profile])
currency (rf/sub [:profile/currency])
{token-symbol :symbol
:as token} (rf/sub [:wallet/wallet-send-token])
network (rf/sub [:wallet/send-network])
{:keys [total-balance]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
prices-per-token (rf/sub [:wallet/prices-per-token])
usd-conversion-rate (utils/token-usd-price token prices-per-token)
currency (rf/sub [:profile/currency])
token-decimals (rf/sub [:wallet/send-display-token-decimals])
conversion-rate (utils/token-price-by-symbol prices-per-token
token-symbol
currency)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(rf/sub [:wallet/send-amount-fixed
(/ input-value conversion-rate)]))
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
unsupported-token-in-receiver? (and (not= tx-type :tx/bridge)
(->> receiver-network-values
(remove #(= (:type %) :add))
(every? #(= (:type %) :not-available))))
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
routes (when suggested-routes
(or (:best suggested-routes) []))
no-routes-found? (and
(every-network-value-is-zero? sender-network-values)
(some? routes)
(not loading-routes?)
(not unsupported-token-in-receiver?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub [:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-network? (set receiver-preferred-networks)
sending-to-unpreferred-networks? (some (comp not receiver-preferred-network?)
receiver-networks)
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:no-routes-found? no-routes-found?
:limit-exceeded? limit-exceeded?
:sender-network-values sender-network-values})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? routes))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
[(:chain-id network)]])
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
[(:chain-id network)]])
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
token-decimals (rf/sub [:wallet/send-display-token-decimals])
prices-per-token (rf/sub [:wallet/prices-per-token])
[crypto-currency?
set-crypto-currency] (rn/use-state initial-crypto-currency?)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
token-balance (or default-limit-crypto total-balance)
usd-conversion-rate (utils/token-usd-price token prices-per-token)
conversion-rate (utils/token-price-by-symbol prices-per-token
token-symbol
currency)
clear-input! #(set-input-state controlled-input/delete-all)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(rf/sub [:wallet/send-amount-fixed
(/ input-value conversion-rate)]))
routes-request-uuid (when suggested-routes
(:uuid suggested-routes))
no-routes-found? (and
routes-request-uuid
(empty? route)
(not loading-routes?))
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:limit-exceeded? limit-exceeded?})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
on-confirm (or default-on-confirm handle-on-confirm)
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? route))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
(rn/use-effect
(fn []
(when active-screen?
@ -282,13 +219,13 @@
(rn/use-effect
(fn []
(clear-input!)
(rf/dispatch [:wallet/stop-and-clean-suggested-routes])
(rf/dispatch [:wallet/clean-disabled-from-networks]))
(rf/dispatch [:wallet/stop-and-clean-suggested-routes]))
[current-address])
(rn/use-effect
(fn []
(request-fetch-routes 0))
[send-from-locked-amounts])
(when active-screen?
(request-fetch-routes 2000)))
[amount-in-crypto valid-input?])
[rn/view
{:style style/screen
:accessibility-label (str "container"
@ -316,7 +253,7 @@
conversion-rate)
conversion-rate))
:hint-component [quo/network-tags
{:networks (seq from-enabled-networks)
{:networks (seq [network])
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
@ -328,24 +265,16 @@
(controlled-input/upper-limit-bn
input-state)))})
:status (when (controlled-input/input-error input-state) :error)}]}]
[routes/view
{:token token-by-symbol
:input-value input-value
:valid-input? valid-input?
:token-not-supported-in-receiver-networks? unsupported-token-in-receiver?
:current-screen-id current-screen-id
:request-fetch-routes request-fetch-routes}]
(when (and (not loading-routes?)
sender-network-values
unsupported-token-in-receiver?)
[token-not-available token-symbol receiver-networks token-networks])
(if (= tx-type :tx/bridge)
[routes/view]
[rn/view {:style {:flex 1}}])
(when not-enough-asset?
[not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route))
not-enough-asset?)
(when (or loading-routes? route)
[estimated-fees/view
{:loading-routes? loading-routes?
:fees fee-formatted}])
{:not-enough-asset? not-enough-asset?
:loading-routes? loading-routes?
:fees fee-formatted}])
(when show-no-routes?
[no-routes-found])
[quo/bottom-actions
@ -359,14 +288,10 @@
loading-routes?
(and (not should-try-again?)
confirm-disabled?))
:on-press (cond
should-try-again?
:on-press (if should-try-again?
#(rf/dispatch [:wallet/start-get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount-in-crypto))
:customization-color current-color}
(when should-try-again?

View File

@ -34,19 +34,3 @@
:top margin-top}
inverted?
(assoc :transform [{:scaleY -1}])))
(def disclaimer
{:margin-horizontal 20
:margin-top 20
:margin-bottom 8})
(def input-container
{:margin-top 8
:margin-bottom 12})
(defn keyboard-container
[bottom]
{:padding-bottom bottom})
(def error-box
{:margin-horizontal 20})

View File

@ -1,122 +1,39 @@
(ns status-im.contexts.wallet.send.routes.view
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.routes.style :as style]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-preferences.view :as network-preferences]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def row-height 44)
(def space-between-rows 11)
(def network-link-linear-height 10)
(def network-link-1x-height 56)
(def network-link-2x-height 111)
(defn- open-preferences
[]
(rf/dispatch
[:show-bottom-sheet
{:content
(fn []
(let [receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub
[:wallet/wallet-send-receiver-preferred-networks])
{token-symbol :symbol
token-networks :supported-networks} (rf/sub [:wallet/wallet-send-token])
token-chain-ids-set (set (mapv #(:chain-id %) token-networks))
[selected-receiver-networks
set-selected-receiver-networks] (rn/use-state receiver-networks)
receiver-preferred-networks-set (set receiver-preferred-networks)
receiver-selected-preferred-networks (filter #(contains?
receiver-preferred-networks-set
%)
selected-receiver-networks)
receiver-selected-non-preferred-networks (filter #(not (contains?
receiver-preferred-networks-set
%))
selected-receiver-networks)
not-available-preferred-networks (filter (fn [preferred-chain-id]
(not (contains? token-chain-ids-set
preferred-chain-id)))
receiver-selected-preferred-networks)
not-available-non-preferred-networks (filter (fn [preferred-chain-id]
(not (contains?
token-chain-ids-set
preferred-chain-id)))
receiver-selected-non-preferred-networks)
first-section-warning-label (when (not-empty not-available-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-preferred-networks)}))
second-section-warning-label (when (not-empty
not-available-non-preferred-networks)
(i18n/label
:t/token-not-available-on-networks
{:token-symbol token-symbol
:networks
(network-utils/network-ids->formatted-text
not-available-non-preferred-networks)}))]
[network-preferences/view
{:title (i18n/label :t/edit-receiver-networks)
:first-section-label (i18n/label :t/preferred-by-receiver)
:second-section-label (i18n/label :t/not-preferred-by-receiver)
:selected-networks (set (map network-utils/id->network
receiver-networks))
:receiver-preferred-networks receiver-preferred-networks
:button-label (i18n/label :t/apply-changes)
:first-section-warning-label first-section-warning-label
:second-section-warning-label second-section-warning-label
:on-change #(set-selected-receiver-networks %)
:on-save (fn [chain-ids]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}]))
(defn render-network-values
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[{:keys [network-values token-symbol token-decimals receiver?]}]
[rn/view
(doall
(map-indexed (fn [index
{chain-id :chain-id
network-value-type :type
total-amount :total-amount}]
(let [status (cond (and (= network-value-type :not-available)
loading-routes?
token-not-supported-in-receiver-networks?)
:loading
(= network-value-type :not-available)
:disabled
:else network-value-type)
amount-formatted (-> (rf/sub [:wallet/send-amount-fixed total-amount])
(let [amount-formatted (-> total-amount
(common-utils/sanitized-token-amount-to-display
token-decimals)
(str " " token-symbol))]
[rn/view
{:key (str (if receiver? "to" "from") "-" chain-id)
:style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge
{:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available)
amount-formatted)
:network (network-utils/id->network chain-id)
:status status
:on-press #(when (not loading-routes?)
(cond
(= network-value-type :edit)
(open-preferences)
on-press (on-press chain-id total-amount)))
:on-long-press #(when (and (not loading-routes?) (not= status :disabled))
(cond
(= network-value-type :add)
(open-preferences)
on-long-press (on-long-press chain-id total-amount)))}]]))
{:amount amount-formatted
:network (network-utils/id->network chain-id)
:status network-value-type}]]))
network-values))])
(defn render-network-links
@ -156,59 +73,14 @@
:destination destination}]]]))
network-links)])
(defn disable-chain
[chain-id disabled-from-chain-ids token-available-networks-for-suggested-routes]
(let [disabled-chain-ids
(if (contains? (set
disabled-from-chain-ids)
chain-id)
(vec (remove #(= % chain-id)
disabled-from-chain-ids))
(conj disabled-from-chain-ids
chain-id))
re-enabling-chain?
(< (count disabled-chain-ids)
(count disabled-from-chain-ids))]
(if (or re-enabling-chain?
(> (count token-available-networks-for-suggested-routes) 1))
(rf/dispatch [:wallet/disable-from-networks
disabled-chain-ids])
(rf/dispatch [:toasts/upsert
{:id :disable-chain-error
:type :negative
:text (i18n/label :t/at-least-one-network-must-be-activated)}]))))
(defn view
[{:keys [token theme valid-input? request-fetch-routes on-press-to-network current-screen-id
token-not-supported-in-receiver-networks? input-value]}]
(let [token-symbol (:symbol token)
nav-current-screen-id (rf/sub [:view-id])
active-screen? (= nav-current-screen-id current-screen-id)
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub
[:wallet/wallet-send-receiver-network-values])
network-links (rf/sub [:wallet/wallet-send-network-links])
disabled-from-chain-ids (rf/sub
[:wallet/wallet-send-disabled-from-chain-ids])
{token-balances-per-chain :balances-per-chain} (rf/sub [:wallet/wallet-send-token])
token-available-networks-for-suggested-routes
(send-utils/token-available-networks-for-suggested-routes
{:balances-per-chain token-balances-per-chain
:disabled-chain-ids disabled-from-chain-ids})
show-routes? (not-empty sender-network-values)]
(rn/use-effect
(fn []
(when (and active-screen?
(> (count token-available-networks-for-suggested-routes) 0))
(request-fetch-routes 2000)))
[input-value valid-input?])
(rn/use-effect
#(when (and active-screen? (> (count token-available-networks-for-suggested-routes) 0))
(request-fetch-routes 0))
[disabled-from-chain-ids])
[]
(let [token-symbol (rf/sub [:wallet/wallet-send-token-symbol])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
network-links (rf/sub [:wallet/wallet-send-network-links])
token-decimals (rf/sub [:wallet/send-display-token-decimals])
show-routes? (not-empty sender-network-values)]
[rn/scroll-view {:content-container-style style/routes-container}
(when show-routes?
[rn/view {:style style/routes-header-container}
@ -220,27 +92,16 @@
:container-style style/section-label-right}]])
[rn/view {:style style/routes-inner-container}
[render-network-values
{:token-symbol token-symbol
:network-values sender-network-values
:on-press (fn [chain-id-to-disable]
(disable-chain
chain-id-to-disable
disabled-from-chain-ids
token-available-networks-for-suggested-routes))
:receiver? false
:theme theme
:loading-routes? loading-routes?
:token-not-supported-in-receiver-networks? false}]
{:token-symbol token-symbol
:network-values sender-network-values
:token-decimals token-decimals
:receiver? false}]
[render-network-links
{:network-links network-links
:sender-network-values sender-network-values}]
[render-network-values
{:token-symbol token-symbol
:network-values receiver-network-values
:on-press on-press-to-network
:receiver? true
:loading-routes? loading-routes?
:theme theme
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:on-save #(request-fetch-routes 0)}]]]))
{:token-symbol token-symbol
:network-values receiver-network-values
:token-decimals token-decimals
:receiver? true}]]]))

View File

@ -176,7 +176,6 @@
(rf/dispatch [:wallet/clean-local-suggestions])
(rf/dispatch [:wallet/clean-selected-collectible])
(rf/dispatch [:wallet/clean-send-address])
(rf/dispatch [:wallet/clean-disabled-from-networks])
(rf/dispatch [:wallet/select-address-tab nil])
(rf/dispatch [:wallet/clean-current-viewing-account
:ignore-just-complete-transaction]))

View File

@ -78,7 +78,7 @@
:on-press on-close
:switcher-type :select-account}]
[quo/page-top
{:title (i18n/label :t/select-asset)
{:title (i18n/label :t/select-token)
:title-accessibility-label :title-label}]
[quo/segmented-control
{:size 32

View File

@ -8,12 +8,8 @@
(defn view
[]
[input-amount/view
{:current-screen-id :screen/wallet.send-input-amount
:button-one-label (i18n/label :t/review-send)
:enabled-from-chain-ids (rf/sub [:wallet/wallet-send-enabled-from-chain-ids])
:from-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
:on-navigate-back (fn []
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
(rf/dispatch [:wallet/clean-disabled-from-networks])
(rf/dispatch [:wallet/clean-from-locked-amounts])
(rf/dispatch [:wallet/clean-send-amount]))}])
{:current-screen-id :screen/wallet.send-input-amount
:button-one-label (i18n/label :t/review-send)
:on-navigate-back (fn []
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
(rf/dispatch [:wallet/clean-send-amount]))}])

View File

@ -48,11 +48,10 @@
(defn- path-amount-out
[path]
(if (= (:bridge-name path) constants/bridge-name-hop)
(let [{:keys [token-fees bonder-fees amount-in]} path]
(let [{:keys [token-fees amount-in]} path]
(-> amount-in
money/from-hex
(money/sub token-fees)
(money/add bonder-fees)))
(money/sub token-fees)))
(-> path :amount-out money/from-hex)))
(defn convert-wei-to-eth
@ -138,12 +137,6 @@
:optimism 2
:arbitrum 3})
(defn safe-add-type-edit
[network-values]
(if (or (empty? network-values) (some #(= (:type %) :edit) network-values))
network-values
(conj network-values {:type :edit})))
(defn reset-loading-network-amounts-to-zero
[network-amounts]
(mapv
@ -155,118 +148,39 @@
network-amounts))
(defn reset-network-amounts-to-zero
[{:keys [network-amounts disabled-chain-ids]}]
[network-amounts]
(map
(fn [network-amount]
(let [disabled-chain-ids-set (set disabled-chain-ids)
disabled? (contains? disabled-chain-ids-set
(:chain-id network-amount))]
(cond-> network-amount
(and (not= (:type network-amount) :edit)
(not= (:type network-amount) :not-available))
(assoc :total-amount (money/bignumber "0")
:type (if disabled? :disabled :default)))))
(assoc network-amount
:total-amount (money/bignumber "0")
:type :default))
network-amounts))
(defn network-amounts
[{:keys [network-values disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?
from-locked-amounts]}]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
network-values-keys (set (keys network-values))
routes-found? (pos? (count network-values-keys))
token-networks-ids-set (set token-networks-ids)
not-available-networks (if receiver?
(filter #(not (token-networks-ids-set %))
receiver-networks)
[])
not-available-networks-set (set not-available-networks)
network-values-with-disabled-chains (when routes-found?
(reduce
(fn [acc k]
(if (or (contains? network-values-keys k)
receiver?)
acc
(assoc acc k (money/bignumber "0"))))
network-values
disabled-chain-ids))
network-values-with-not-available-chains (if (and receiver? routes-found?)
(let [network-values-keys
(set (keys
network-values-with-disabled-chains))]
(reduce
(fn [acc k]
(if (not (contains? network-values-keys k))
(assoc acc k nil)
acc))
network-values-with-disabled-chains
not-available-networks))
network-values-with-disabled-chains)]
(cond-> (->>
network-values-with-not-available-chains
(map
(fn [[chain-id amount]]
{:chain-id chain-id
:total-amount (get from-locked-amounts chain-id amount)
:type (cond
(contains? from-locked-amounts chain-id) :locked
(contains? not-available-networks-set chain-id) :not-available
(or receiver? (not (contains? disabled-set chain-id))) :default
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}))
(sort-by (fn [network-amount]
(get network-priority-score
(network-utils/id->network (:chain-id network-amount)))))
(filter
(fn [network-amount]
(or (and receiver?
(or (contains? receiver-networks-set (:chain-id network-amount))
(money/above-zero? (:total-amount network-amount))))
(not receiver?))))
(vec))
(and receiver?
routes-found?
(not= tx-type :tx/bridge))
safe-add-type-edit)))
[network-values]
(->> network-values
(map
(fn [[chain-id amount]]
{:chain-id chain-id
:total-amount amount
:type :default}))
(sort-by (fn [network-amount]
(get network-priority-score
(network-utils/id->network (:chain-id network-amount)))))
(vec)))
(defn loading-network-amounts
[{:keys [valid-networks disabled-chain-ids receiver-networks token-networks-ids tx-type receiver?]}]
(let [disabled-set (set disabled-chain-ids)
receiver-networks-set (set receiver-networks)
token-networks-ids-set (set token-networks-ids)
valid-networks-set (set valid-networks)
not-available-networks (if receiver?
(filter #(not (token-networks-ids-set %)) receiver-networks)
[])
not-available-networks-set (set not-available-networks)
valid-networks (-> (concat valid-networks
(when (not (and receiver? (= tx-type :tx/bridge)))
disabled-chain-ids)
(when receiver?
(filter #(not (valid-networks-set %))
not-available-networks)))
(distinct))]
(->> valid-networks
(map
(fn [chain-id]
(cond->
{:chain-id chain-id
:type (cond
(contains? not-available-networks-set chain-id) :not-available
(or receiver?
(not (contains? disabled-set chain-id))) :loading
(and (not receiver?) (contains? disabled-set chain-id)) :disabled)}
(and (not receiver?) (contains? disabled-set chain-id))
(assoc :total-amount (money/bignumber "0")))))
(filter
(fn [network-amount]
(or (and receiver?
(or (= tx-type :tx/bridge)
(contains? receiver-networks-set (:chain-id network-amount))))
(not receiver?))))
(sort-by (fn [network-amount]
(get network-priority-score
(network-utils/id->network (:chain-id network-amount)))))
(vec))))
[{:keys [networks values receiver?]}]
(->> networks
(map
(fn [chain-id]
(let [network-value (when values (get values chain-id))]
(cond-> {:chain-id chain-id
:type (if network-value :default :loading)}
network-value (assoc :total-amount
(money/bignumber network-value))
(and (not network-value) (not receiver?)) (assoc :total-amount (money/bignumber "0"))))))
(vec)))
(defn network-links
[route from-values-by-chain to-values-by-chain]

View File

@ -85,11 +85,9 @@
(testing "Correctly calculates network (out) amounts for bridge transaction"
(let [route [{:bridge-name "Hop"
:amount-in "0xde0b6b3a7640000"
:bonder-fees (money/bignumber "200000000000000")
:token-fees (money/bignumber "230000000000000")
:to {:chain-id 1}}
{:bridge-name "Hop"
:bonder-fees (money/bignumber "300000000000000")
:token-fees (money/bignumber "410000000000000")
:amount-in "0xde0b6b3a7640000"
:to {:chain-id 10}}]
@ -100,8 +98,8 @@
:token-decimals token-decimals
:native-token? native-token?
:receiver? receiver?})
expected {1 (money/bignumber "0.99997")
10 (money/bignumber "0.99989")}]
expected {1 (money/bignumber "0.99977")
10 (money/bignumber "0.99959")}]
(doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value))))))
@ -350,171 +348,12 @@
(is (every? identity comparisons)))))
(deftest network-amounts-test
(testing "Handles disabled and receiver networks correctly when receiver? is true"
(let [network-values {10 (money/bignumber "200")}
disabled-chain-ids [1]
receiver-networks [10]
token-networks-ids [1 10 42161]
receiver? true
expected [{:chain-id 10
:total-amount (money/bignumber "200")
:type :default}
{:type :edit}]
tx-type :tx/send
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing "Adds default amount for non-disabled non-receiver networks when receiver? is false"
(let [network-values {1 (money/bignumber "100")}
disabled-chain-ids [10]
receiver-networks []
token-networks-ids [1 10 42161]
receiver? false
expected [{:chain-id 1
:total-amount (money/bignumber "100")
:type :default}
{:chain-id 10
:total-amount (money/bignumber "0")
:type :disabled}]
tx-type :tx/send
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing "Handles empty inputs correctly"
(let [network-values {}
disabled-chain-ids []
receiver-networks []
token-networks-ids []
receiver? true
expected []
tx-type :tx/send
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(is (= expected result))))
(testing "Processes case with multiple network interactions"
(let [network-values {1 (money/bignumber "300")
10 (money/bignumber "400")
42161 (money/bignumber "500")}
disabled-chain-ids [1 42161]
receiver-networks [10]
token-networks-ids [1 10 42161]
receiver? true
expected [{:chain-id 1
:total-amount (money/bignumber "300")
:type :default}
{:chain-id 10
:total-amount (money/bignumber "400")
:type :default}
{:chain-id 42161
:total-amount (money/bignumber "500")
:type :default}
{:type :edit}]
tx-type :tx/send
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing "Does not assign :not-available type when receiver? is false"
(let [network-values {1 (money/bignumber "100")}
disabled-chain-ids [10]
receiver-networks [1]
token-networks-ids [1 10]
receiver? false
expected [{:chain-id 1
:total-amount (money/bignumber "100")
:type :default}
{:chain-id 10
:total-amount (money/bignumber "0")
:type :disabled}]
tx-type :tx/send
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing
"Assigns :not-available type to networks not available in token-networks-ids when receiver? is true"
(let [network-values {1 (money/bignumber "100")}
disabled-chain-ids []
receiver-networks [1 10]
token-networks-ids [1]
receiver? false
expected [{:chain-id 1
:total-amount (money/bignumber "100")
:type :default}
{:chain-id 10
:total-amount nil
:type :not-available}]
tx-type :tx/send
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing
"Handles disabled and receiver networks correctly when receiver? is false and tx-type is :tx/bridge"
(let [network-values {10 (money/bignumber "200")}
disabled-chain-ids [1]
receiver-networks [10]
token-networks-ids [1 10]
tx-type :tx/bridge
receiver? false
expected [{:chain-id 1
:total-amount (money/bignumber "0")
:type :disabled}
{:chain-id 10
:total-amount (money/bignumber "200")
:type :default}]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
(testing
"Handles disabled and receiver networks correctly when receiver? is true and tx-type is :tx/bridge"
(let [network-values {10 (money/bignumber "200")}
disabled-chain-ids [1]
receiver-networks [10]
token-networks-ids [1 10]
tx-type :tx/bridge
receiver? true
expected [{:chain-id 10
:total-amount (money/bignumber "200")
:type :default}]
result (utils/network-amounts {:network-values network-values
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})]
(testing "Handles network values with one chain"
(let [network-values {10 (money/bignumber "200")}
expected [{:chain-id 10
:total-amount (money/bignumber "200")
:type :default}]
result (utils/network-amounts network-values)]
(is (every? identity (map #(map/deep-compare %1 %2) expected result))))))
(deftest loading-network-amounts-test
@ -538,27 +377,6 @@
result)]
(is (every? identity comparisons))))
(testing "Assigns :disabled type with zero total-amount to disabled networks when receiver? is false"
(let [valid-networks [1 10 42161]
disabled-chain-ids [10 42161]
receiver-networks [1]
token-networks-ids [1 10 42161]
receiver? false
expected [{:chain-id 1 :type :loading}
{:chain-id 10 :type :disabled :total-amount (money/bignumber "0")}
{:chain-id 42161 :type :disabled :total-amount (money/bignumber "0")}]
tx-type :tx/send
result (utils/loading-network-amounts {:valid-networks valid-networks
:disabled-chain-ids disabled-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? receiver?})
comparisons (map #(map/deep-compare %1 %2)
expected
result)]
(is (every? identity comparisons))))
(testing "Filters out networks not in receiver networks when receiver? is true"
(let [valid-networks [1 10 42161 59144]
disabled-chain-ids [10]

View File

@ -8,12 +8,14 @@
[utils.re-frame :as rf]))
(defn- network-item
[{:keys [network on-select-network]}]
(let [{:keys [network-name
chain-id]} network
[{:keys [network on-select-network source]}]
(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)]
balance-in-fiat :fiat} (if (= source :swap)
(rf/sub [:wallet/swap-asset-to-pay-network-balance chain-id])
(rf/sub [:wallet/send-token-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)
@ -23,21 +25,24 @@
: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])
[{:keys [token-symbol on-select-network source title]
:or {source :swap
title (i18n/label :t/select-network)}}]
(let [{mainnet-network :mainnet-network
layer-2-networks :layer-2-networks} (if (= source :swap)
(rf/sub [:wallet/swap-asset-to-pay-networks])
(rf/sub [:wallet/send-token-grouped-networks]))
render-fn (rn/use-callback (fn [network]
[network-item
{:network network
:on-select-network
on-select-network}]))]
{:network network
:on-select-network on-select-network
:source source}]))]
[:<>
[rn/view {:style style/header-container}
[quo/text
{:size :heading-2
:weight :semi-bold}
(i18n/label :t/select-network)]
title]
[quo/context-tag
{:type :token
:size 24
@ -46,7 +51,8 @@
(when mainnet-network
[network-item
{:network mainnet-network
:on-select-network on-select-network}])
:on-select-network on-select-network
:source source}])
[quo/divider-label {:container-style style/divider-label}
(i18n/label :t/layer-2)]
[rn/flat-list

View File

@ -34,7 +34,8 @@
(defn view
[]
(let [accounts (rf/sub [:wallet/accounts-with-balances])]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
accounts (rf/sub [:wallet/accounts-with-balances asset-to-pay])]
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav

View File

@ -56,12 +56,12 @@
(re-frame/reg-sub
:wallet/network-values
:<- [:wallet/wallet-send]
:<- [:wallet/send-display-token-decimals]
(fn [[{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} token-decimals]
(fn [{:keys [from-values-by-chain to-values-by-chain token-display-name token] :as send-data}
[_ to-values?]]
(let [network-values (if to-values? to-values-by-chain from-values-by-chain)
token-symbol (or token-display-name
(-> send-data :token :symbol))]
(-> send-data :token :symbol))
token-decimals (:decimals token)]
(reduce-kv
(fn [acc chain-id amount]
(let [network-name (network-utils/id->network chain-id)

View File

@ -2,7 +2,7 @@
(:require
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-tab-constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money]
@ -34,6 +34,11 @@
:<- [:wallet/wallet-send]
:-> :token)
(rf/reg-sub
:wallet/send-token-symbol
:<- [:wallet/wallet-send]
:-> :token-symbol)
(rf/reg-sub
:wallet/send-transaction-ids
:<- [:wallet/wallet-send]
@ -54,6 +59,11 @@
:<- [:wallet/wallet-send]
:-> :tx-type)
(rf/reg-sub
:wallet/send-network
:<- [:wallet/wallet-send]
:-> :network)
(rf/reg-sub
:wallet/sending-collectible?
:<- [:wallet/send-tx-type]
@ -77,7 +87,7 @@
(->> address-activity
(sort :timestamp)
(keep (fn [{:keys [activity-type recipient]}]
(when (= activity-constants/wallet-activity-type-send activity-type)
(when (= activity-tab-constants/wallet-activity-type-send activity-type)
recipient)))
(distinct)))))
@ -173,3 +183,39 @@
(= (:chain-id network) bridge-to-chain-id)
network))
networks))))
(rf/reg-sub
:wallet/send-token-grouped-networks
:<- [:wallet/wallet-send-token]
(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/send-token-network-balance
:<- [:wallet/wallet-send-token]
:<- [:profile/currency]
:<- [:profile/currency-symbol]
:<- [:wallet/prices-per-token]
(fn [[token currency currency-symbol prices-per-token] [_ 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 (common-utils/calculate-token-fiat-value
{:currency currency
:balance total-balance
:token token
:prices-per-token prices-per-token})
crypto-formatted (common-utils/get-standard-crypto-format token
total-balance
prices-per-token)
fiat-formatted (common-utils/fiat-formatted-for-ui currency-symbol
fiat-value)]
{:crypto (str crypto-formatted " " (:symbol token))
:fiat fiat-formatted})))

View File

@ -3,7 +3,6 @@
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money]
[utils.number :as number]))
@ -334,24 +333,3 @@
:token token-for-fees
:prices-per-token prices-per-token})]
fee-in-fiat)))
(rf/reg-sub
:wallet/accounts-with-balances
:<- [:wallet/operable-accounts]
:<- [:wallet/swap-asset-to-pay]
(fn [[accounts asset-to-pay]]
(let [token-symbol (:symbol asset-to-pay)]
(map
(fn [account]
(let [tokens (:tokens account)
filtered-tokens (filter #(= (:symbol %) token-symbol) tokens)
asset-pay-balance (utils/calculate-total-token-balance filtered-tokens)
formatted-address (network-utils/format-address (:address account)
(:network-preferences-names account))]
(assoc account
:formatted-address formatted-address
:asset-pay-balance (utils/sanitized-token-amount-to-display
asset-pay-balance
constants/min-token-decimals-to-display)
:asset-pay-symbol token-symbol)))
accounts))))

View File

@ -152,21 +152,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-receiver-networks
:<- [:wallet/wallet-send]
:-> :receiver-networks)
(rf/reg-sub
:wallet/wallet-send-receiver-preferred-networks
:<- [:wallet/wallet-send]
:-> :receiver-preferred-networks)
(rf/reg-sub
:wallet/wallet-send-route
:<- [:wallet/wallet-send]
@ -181,14 +171,11 @@
:wallet/wallet-send-token
:<- [:wallet/wallet-send]
:<- [:wallet/network-details]
:<- [:wallet/wallet-send-disabled-from-chain-ids]
(fn [[wallet-send networks disabled-from-chain-ids]]
(let [token (:token wallet-send)
disabled-from-chain-ids? (set disabled-from-chain-ids)
enabled-from-chain-ids (->> networks
(map :chain-id)
(remove disabled-from-chain-ids?)
set)]
(fn [[wallet-send networks]]
(let [token (:token wallet-send)
enabled-from-chain-ids (->> networks
(map :chain-id)
set)]
(some-> token
(assoc :networks (network-utils/network-list-with-positive-balance token networks)
:supported-networks (network-utils/network-list token networks)
@ -203,16 +190,6 @@
(fn [{:keys [token-symbol token]}]
(or token-symbol (:symbol token))))
(rf/reg-sub
:wallet/wallet-send-disabled-from-chain-ids
:<- [:wallet/wallet-send]
:-> :disabled-from-chain-ids)
(rf/reg-sub
:wallet/wallet-send-from-locked-amounts
:<- [:wallet/wallet-send]
:-> :from-locked-amounts)
(rf/reg-sub
:wallet/wallet-send-from-values-by-chain
:<- [:wallet/wallet-send]
@ -578,6 +555,28 @@
(string/lower-case token-symbol)))
first)))
(rf/reg-sub
:wallet/token-by-symbol-from-first-available-account-with-balance
:<- [:wallet/accounts]
:<- [:wallet/current-viewing-account-or-default]
:<- [:wallet/network-details]
(fn [[accounts {:keys [tokens]} networks] [_ token-symbol chain-ids]]
(when token-symbol
(or
(->> (utils/tokens-with-balance tokens networks chain-ids)
(filter #(and (= (string/lower-case (:symbol %))
(string/lower-case token-symbol))
(money/greater-than (:total-balance %) 0)))
first)
(some
(fn [{:keys [tokens]}]
(->> (utils/tokens-with-balance tokens networks chain-ids)
(filter #(and (= (string/lower-case (:symbol %))
(string/lower-case token-symbol))
(money/greater-than (:total-balance %) 0)))
first))
accounts)))))
(rf/reg-sub
:wallet/accounts-without-current-viewing-account
:<- [:wallet/accounts]
@ -831,22 +830,6 @@
:<- [:wallet/create-account]
:-> :public-address)
(rf/reg-sub
:wallet/wallet-send-enabled-networks
:<- [:wallet/wallet-send-token]
:<- [:wallet/wallet-send-disabled-from-chain-ids]
(fn [[{:keys [networks]} disabled-from-chain-ids]]
(->> networks
(filter #(not (contains? (set disabled-from-chain-ids)
(:chain-id %))))
set)))
(rf/reg-sub
:wallet/wallet-send-enabled-from-chain-ids
:<- [:wallet/wallet-send-enabled-networks]
(fn [send-enabled-networks]
(map :chain-id send-enabled-networks)))
(rf/reg-sub
:wallet/wallet-send-fee-fiat-formatted
:<- [:wallet/current-viewing-account]
@ -909,3 +892,23 @@
:<- [:wallet/accounts-without-current-viewing-account]
(fn [accounts]
(get-emoji-and-colors-from-accounts accounts)))
(rf/reg-sub
:wallet/accounts-with-balances
:<- [:wallet/operable-accounts]
(fn [accounts [_ token]]
(let [token-symbol (:symbol token)]
(map
(fn [account]
(let [tokens (:tokens account)
filtered-tokens (filter #(= (:symbol %) token-symbol) tokens)
asset-pay-balance (utils/calculate-total-token-balance filtered-tokens)
formatted-address (network-utils/format-address (:address account)
(:network-preferences-names account))]
(assoc account
:formatted-address formatted-address
:asset-pay-balance (utils/sanitized-token-amount-to-display
asset-pay-balance
constants/min-token-decimals-to-display)
:asset-pay-symbol token-symbol)))
accounts))))

View File

@ -226,7 +226,7 @@ class TestFallbackMultipleDevice(MultipleSharedDeviceTestCase):
self.profile_2.click_system_back_button(times=3)
wallet_2.just_fyi("Device 2: check wallet balance")
wallet_2.select_network(network_name='Arbitrum')
wallet_2.set_network_in_wallet(network_name='Arbitrum')
expected_balance = self.network_api.get_balance(key_pair_account_address)
shown_balance = wallet_2.get_asset(asset_name='Ether').get_amount()
if shown_balance != round(expected_balance, 5):

View File

@ -32,6 +32,7 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
self.wallet_1, self.wallet_2 = self.sign_in_1.get_wallet_view(), self.sign_in_2.get_wallet_view()
self.wallet_1.wallet_tab.click()
self.wallet_2.wallet_tab.click()
self.network = "Arbitrum"
def _get_balances_before_tx(self):
sender_balance = self.network_api.get_balance(self.sender['wallet_address'])
@ -85,8 +86,8 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
(self.home_2.reopen_app, {'user_name': self.receiver_username}))))
self.wallet_1.wallet_tab.wait_and_click()
self.wallet_2.wallet_tab.wait_and_click()
self.wallet_1.select_network(network_name='Arbitrum')
self.wallet_2.select_network(network_name='Arbitrum')
self.wallet_1.set_network_in_wallet(network_name=self.network)
self.wallet_2.set_network_in_wallet(network_name=self.network)
self.loop.run_until_complete(
run_in_parallel(((wait_for_wallet_balance_to_update, {'wallet_view': self.wallet_1,
'user_name': self.sender_username,
@ -123,8 +124,8 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
@marks.testrail_id(727229)
def test_wallet_send_eth(self):
self.wallet_1.select_network(network_name='Arbitrum')
self.wallet_2.select_network(network_name='Arbitrum')
self.wallet_1.set_network_in_wallet(network_name=self.network)
self.wallet_2.set_network_in_wallet(network_name=self.network)
sender_balance, receiver_balance, eth_amount_sender, eth_amount_receiver = self._get_balances_before_tx()
self.wallet_2.close_account_button.click()
@ -135,7 +136,8 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
device_time_before_sending = self.wallet_1.driver.device_time
self.wallet_1.send_asset(address='arb1:' + self.receiver['wallet_address'],
asset_name='Ether',
amount=amount_to_send)
amount=amount_to_send,
network_name=self.network)
self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
tx_time=device_time_before_sending)
@ -167,7 +169,8 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
device_time_before_sending = self.wallet_1.driver.device_time
self.wallet_1.send_asset_from_drawer(address='arb1:' + self.receiver['wallet_address'],
asset_name='Ether',
amount=amount_to_send)
amount=amount_to_send,
network_name=self.network)
self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
tx_time=device_time_before_sending)
device_time_after_sending = self.wallet_1.driver.device_time
@ -234,7 +237,7 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
for network in expected_balances:
self.wallet_view.just_fyi("Checking total balance on %s network" % network)
self.wallet_view.select_network(network)
self.wallet_view.set_network_in_wallet(network)
real_balance = {}
for asset in expected_balances[network]:
real_balance[asset] = self.wallet_view.get_asset(asset).get_amount()
@ -242,7 +245,7 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
if real_balance[asset] != expected_balances[network][asset]:
self.errors.append("For the %s the wrong value %s is shown, expected %s on %s" %
(asset, real_balance[asset], expected_balances[network][asset], network))
self.wallet_view.select_network(network)
self.wallet_view.set_network_in_wallet(network)
self.errors.verify_no_errors()

View File

@ -113,7 +113,7 @@ class WalletView(BaseView):
self.key_pair_name_input = EditBox(
self.driver, xpath="//*[@text='Key pair name']/..//following-sibling::*/*[@content-desc='input']")
def select_network(self, network_name: str):
def set_network_in_wallet(self, network_name: str):
self.network_drop_down.click()
Button(self.driver, accessibility_id="%s, label-component" % network_name.capitalize()).click()
self.network_drop_down.click()
@ -127,8 +127,12 @@ class WalletView(BaseView):
return element
def select_asset(self, asset_name: str):
return Button(driver=self.driver,
xpath="//*[@content-desc='token-network']/android.widget.TextView[@text='%s']" % asset_name)
Button(driver=self.driver,
xpath="//*[@content-desc='token-network']/android.widget.TextView[@text='%s']" % asset_name).click()
def select_network(self, network_name: str):
Button(driver=self.driver,
xpath="//*[@content-desc='network-list']/*[@text='%s']" % network_name).click()
def slide_and_confirm_with_password(self):
self.slide_button_track.slide()
@ -148,27 +152,23 @@ class WalletView(BaseView):
for i in '{:f}'.format(amount).rstrip('0'):
Button(self.driver, accessibility_id='keyboard-key-%s' % i).click()
def disable_mainnet_in_from_network(self):
if self.from_network_text.text == 'Mainnet':
self.from_network_text.click()
def send_asset(self, address: str, asset_name: str, amount: float):
def send_asset(self, address: str, asset_name: str, amount: float, network_name: str):
self.send_button.click()
self.address_text_input.send_keys(address)
self.continue_button.click()
self.select_asset(asset_name).click()
self.select_asset(asset_name)
self.select_network(network_name)
self.set_amount(amount)
self.disable_mainnet_in_from_network()
self.confirm_transaction()
def send_asset_from_drawer(self, address: str, asset_name: str, amount: float):
def send_asset_from_drawer(self, address: str, asset_name: str, amount: float, network_name: str):
asset_element = self.get_asset(asset_name)
asset_element.long_press_element()
self.send_from_drawer_button.click()
self.select_network(network_name)
self.address_text_input.send_keys(address)
self.continue_button.click()
self.set_amount(amount)
self.disable_mainnet_in_from_network()
self.confirm_transaction()
def add_regular_account(self, account_name: str):

View File

@ -799,7 +799,6 @@
"do-not-cheat": "Don't try to cheat",
"do-not-cheat-description": "These 12 words give access to all of your funds so it is important that you write them in the correct order, take it seriously.",
"do-not-quit": "Do not quit the application or turn off your device. Doing so will lead to data corruption, loss of your Status profile and the inability to use Status.",
"do-not-share": "Do not share",
"documents": "Documents",
"done": "Done",
@ -2287,8 +2286,11 @@
"select-chat": "Select chat to start messaging",
"select-network": "Select network",
"select-network-for-buying": "Select network for buying",
"select-network-to-bridge-from": "Select network to bridge from",
"select-network-to-receive": "Select network to receive",
"select-networks": "Select networks",
"select-new-location-for-keys": "Select a new location to save your private key(s)",
"select-token": "Select token",
"select-token-to-receive": "Select token to receive",
"select-token-to-swap": "Select token to Swap",
"selected": "Selected",