feat(swap): fetch swap proposals (#21040)
Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
parent
40f98f8238
commit
c861d95d69
|
@ -7,6 +7,7 @@
|
||||||
[:map {:closed true}
|
[:map {:closed true}
|
||||||
[:action? {:optional true} [:maybe boolean?]]
|
[:action? {:optional true} [:maybe boolean?]]
|
||||||
[:text {:optional true} [:maybe string?]]
|
[:text {:optional true} [:maybe string?]]
|
||||||
|
[:container-style {:optional true} [:maybe :map]]
|
||||||
[:button-text {:optional true} [:maybe string?]]
|
[:button-text {:optional true} [:maybe string?]]
|
||||||
[:on-button-press {:optional true} [:maybe fn?]]]]]
|
[:on-button-press {:optional true} [:maybe fn?]]]]]
|
||||||
:any])
|
:any])
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
(ns quo.components.banners.alert-banner.style
|
(ns quo.components.banners.alert-banner.style
|
||||||
(:require [quo.foundations.colors :as colors]))
|
(:require [quo.foundations.colors :as colors]))
|
||||||
|
|
||||||
(def container
|
(defn container
|
||||||
{:flex-direction :row
|
[container-style]
|
||||||
:align-items :center
|
(merge
|
||||||
:height 50
|
{:flex-direction :row
|
||||||
:padding-horizontal 20
|
:align-items :center
|
||||||
:padding-vertical 12})
|
:height 50
|
||||||
|
:padding-horizontal 20
|
||||||
|
:padding-vertical 12}
|
||||||
|
container-style))
|
||||||
|
|
||||||
(defn label
|
(defn label
|
||||||
[theme]
|
[theme]
|
||||||
|
|
|
@ -11,12 +11,12 @@
|
||||||
[schema.core :as schema]))
|
[schema.core :as schema]))
|
||||||
|
|
||||||
(defn- view-internal
|
(defn- view-internal
|
||||||
[{:keys [action? text button-text on-button-press]}]
|
[{:keys [action? text button-text container-style on-button-press]}]
|
||||||
(let [theme (quo.theme/use-theme)]
|
(let [theme (quo.theme/use-theme)]
|
||||||
[rn/view
|
[rn/view
|
||||||
{:accessibility-label :alert-banner}
|
{:accessibility-label :alert-banner}
|
||||||
[linear-gradient/linear-gradient
|
[linear-gradient/linear-gradient
|
||||||
{:style style/container
|
{:style (style/container container-style)
|
||||||
:start {:x 0 :y 0}
|
:start {:x 0 :y 0}
|
||||||
:end {:x 0 :y 1}
|
:end {:x 0 :y 1}
|
||||||
:colors [(colors/theme-colors
|
:colors [(colors/theme-colors
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
[:subtitle-type {:optional true} [:maybe [:enum :default :icon :network :account :editable]]]
|
[:subtitle-type {:optional true} [:maybe [:enum :default :icon :network :account :editable]]]
|
||||||
[:size {:optional true} [:maybe [:enum :default :small :large]]]
|
[:size {:optional true} [:maybe [:enum :default :small :large]]]
|
||||||
[:title :string]
|
[:title :string]
|
||||||
[:subtitle {:optional true} [:maybe :string]]
|
[:subtitle {:optional true} [:maybe [:or :string :double]]]
|
||||||
[:custom-subtitle {:optional true} [:maybe fn?]]
|
[:custom-subtitle {:optional true} [:maybe fn?]]
|
||||||
[:icon {:optional true} [:maybe :keyword]]
|
[:icon {:optional true} [:maybe :keyword]]
|
||||||
[:emoji {:optional true} [:maybe :string]]
|
[:emoji {:optional true} [:maybe :string]]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
(ns quo.components.wallet.swap-input.style
|
(ns quo.components.wallet.swap-input.style
|
||||||
(:require [quo.foundations.colors :as colors]
|
(:require [quo.foundations.colors :as colors]
|
||||||
|
[quo.foundations.shadows :as shadows]
|
||||||
[quo.foundations.typography :as typography]))
|
[quo.foundations.typography :as typography]))
|
||||||
|
|
||||||
(defn- border-color
|
(defn- border-color
|
||||||
|
@ -11,11 +12,13 @@
|
||||||
(colors/theme-colors colors/neutral-5 colors/neutral-90 theme))
|
(colors/theme-colors colors/neutral-5 colors/neutral-90 theme))
|
||||||
|
|
||||||
(defn content
|
(defn content
|
||||||
[theme]
|
[typing? theme]
|
||||||
{:border-width 1
|
(merge
|
||||||
:border-radius 16
|
{:border-width 1
|
||||||
:border-color (border-color theme)
|
:border-radius 16
|
||||||
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
|
:border-color (border-color theme)
|
||||||
|
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}
|
||||||
|
(when typing? (shadows/get 1 theme))))
|
||||||
|
|
||||||
(defn row-1
|
(defn row-1
|
||||||
[loading?]
|
[loading?]
|
||||||
|
|
|
@ -19,13 +19,15 @@
|
||||||
[:props
|
[:props
|
||||||
[:map {:closed true}
|
[:map {:closed true}
|
||||||
[:type {:optional true} [:maybe [:enum :pay :receive]]]
|
[:type {:optional true} [:maybe [:enum :pay :receive]]]
|
||||||
[:status {:optional true} [:maybe [:enum :default :disabled :loading]]]
|
[:status {:optional true} [:maybe [:enum :default :typing :disabled :loading]]]
|
||||||
[:token {:optional true} [:maybe :string]]
|
[:token {:optional true} [:maybe :string]]
|
||||||
[:value {:optional true} [:maybe :string]]
|
[:value {:optional true} [:maybe :string]]
|
||||||
[:default-value {:optional true} [:maybe :string]]
|
[:default-value {:optional true} [:maybe :string]]
|
||||||
[:currency-symbol {:optional true} [:maybe :string]]
|
[:currency-symbol {:optional true} [:maybe :string]]
|
||||||
[:fiat-value {:optional true} [:maybe :string]]
|
[:fiat-value {:optional true} [:maybe :string]]
|
||||||
[:show-approval-label? {:optional true} [:maybe :boolean]]
|
[:show-approval-label? {:optional true} [:maybe :boolean]]
|
||||||
|
[:auto-focus? {:optional true} [:maybe :boolean]]
|
||||||
|
[:input-disabled? {:optional true} [:maybe :boolean]]
|
||||||
[:error? {:optional true} [:maybe :boolean]]
|
[:error? {:optional true} [:maybe :boolean]]
|
||||||
[:show-keyboard? {:optional true} [:maybe :boolean]]
|
[:show-keyboard? {:optional true} [:maybe :boolean]]
|
||||||
[:approval-label-props {:optional true} [:maybe approval-label.schema/?schema]]
|
[:approval-label-props {:optional true} [:maybe approval-label.schema/?schema]]
|
||||||
|
@ -33,6 +35,7 @@
|
||||||
[:on-change-text {:optional true} [:maybe fn?]]
|
[:on-change-text {:optional true} [:maybe fn?]]
|
||||||
[:enable-swap? {:optional true} [:maybe :boolean]]
|
[:enable-swap? {:optional true} [:maybe :boolean]]
|
||||||
[:on-swap-press {:optional true} [:maybe fn?]]
|
[:on-swap-press {:optional true} [:maybe fn?]]
|
||||||
|
[:on-input-focus {:optional true} [:maybe fn?]]
|
||||||
[:on-token-press {:optional true} [:maybe fn?]]
|
[:on-token-press {:optional true} [:maybe fn?]]
|
||||||
[:on-max-press {:optional true} [:maybe fn?]]
|
[:on-max-press {:optional true} [:maybe fn?]]
|
||||||
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
|
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
|
||||||
|
@ -41,13 +44,14 @@
|
||||||
|
|
||||||
(defn view-internal
|
(defn view-internal
|
||||||
[{:keys [type status token value fiat-value show-approval-label? error? network-tag-props
|
[{:keys [type status token value fiat-value show-approval-label? error? network-tag-props
|
||||||
approval-label-props default-value enable-swap?
|
approval-label-props default-value auto-focus? input-disabled? enable-swap?
|
||||||
currency-symbol on-change-text show-keyboard?
|
currency-symbol on-change-text show-keyboard?
|
||||||
container-style on-swap-press on-token-press on-max-press]}]
|
container-style on-swap-press on-token-press on-max-press on-input-focus]}]
|
||||||
(let [theme (quo.theme/use-theme)
|
(let [theme (quo.theme/use-theme)
|
||||||
pay? (= type :pay)
|
pay? (= type :pay)
|
||||||
disabled? (= status :disabled)
|
disabled? (= status :disabled)
|
||||||
loading? (= status :loading)
|
loading? (= status :loading)
|
||||||
|
typing? (= status :typing)
|
||||||
controlled-input? (some? value)
|
controlled-input? (some? value)
|
||||||
input-ref (rn/use-ref-atom nil)
|
input-ref (rn/use-ref-atom nil)
|
||||||
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)) [])
|
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)) [])
|
||||||
|
@ -58,7 +62,7 @@
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style container-style
|
{:style container-style
|
||||||
:accessibility-label :swap-input}
|
:accessibility-label :swap-input}
|
||||||
[rn/view {:style (style/content theme)}
|
[rn/view {:style (style/content typing? theme)}
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style (style/row-1 loading?)}
|
{:style (style/row-1 loading?)}
|
||||||
[rn/pressable {:on-press on-token-press}
|
[rn/pressable {:on-press on-token-press}
|
||||||
|
@ -78,7 +82,9 @@
|
||||||
colors/neutral-50
|
colors/neutral-50
|
||||||
theme)
|
theme)
|
||||||
:keyboard-type :numeric
|
:keyboard-type :numeric
|
||||||
:auto-focus true
|
:editable (not input-disabled?)
|
||||||
|
:auto-focus auto-focus?
|
||||||
|
:on-focus on-input-focus
|
||||||
:on-change-text on-change-text
|
:on-change-text on-change-text
|
||||||
:show-soft-input-on-focus show-keyboard?
|
:show-soft-input-on-focus show-keyboard?
|
||||||
:default-value default-value
|
:default-value default-value
|
||||||
|
|
|
@ -551,6 +551,7 @@
|
||||||
(def ^:const send-type-bridge 5)
|
(def ^:const send-type-bridge 5)
|
||||||
(def ^:const send-type-erc-721-transfer 6)
|
(def ^:const send-type-erc-721-transfer 6)
|
||||||
(def ^:const send-type-erc-1155-transfer 7)
|
(def ^:const send-type-erc-1155-transfer 7)
|
||||||
|
(def ^:const send-type-swap 8)
|
||||||
|
|
||||||
(def ^:const multi-transaction-type-send 0)
|
(def ^:const multi-transaction-type-send 0)
|
||||||
(def ^:const multi-transaction-type-approve 1)
|
(def ^:const multi-transaction-type-approve 1)
|
||||||
|
@ -596,3 +597,5 @@
|
||||||
:color :blue
|
:color :blue
|
||||||
:contract-address "0xdef171fe48cf0115b1d80b88dc8eab59176fee57"
|
:contract-address "0xdef171fe48cf0115b1d80b88dc8eab59176fee57"
|
||||||
:terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"})
|
:terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"})
|
||||||
|
|
||||||
|
(def ^:const token-for-fees-symbol "ETH")
|
||||||
|
|
|
@ -518,23 +518,29 @@
|
||||||
|
|
||||||
(rf/reg-event-fx
|
(rf/reg-event-fx
|
||||||
:wallet/handle-suggested-routes
|
:wallet/handle-suggested-routes
|
||||||
(fn [_ [data]]
|
(fn [{:keys [db]} [data]]
|
||||||
(if-let [{:keys [code details]} (-> data :ErrorResponse)]
|
(let [swap? (get-in db [:wallet :ui :swap])
|
||||||
(let [error-message (if (= code "0") "An error occurred" details)]
|
{:keys [code details] :as error-response} (-> data :ErrorResponse)]
|
||||||
(log/error "failed to get suggested routes (async)"
|
(if (and (not swap?) error-response)
|
||||||
{:event :wallet/handle-suggested-routes
|
(let [error-message (if (= code "0") "An error occurred" details)]
|
||||||
:error error-message})
|
(log/error "failed to get suggested routes (async)"
|
||||||
{:fx [[:dispatch [:wallet/suggested-routes-error error-message]]]})
|
{:event :wallet/handle-suggested-routes
|
||||||
(let [best-routes-fix (comp ->old-route-paths
|
:error error-message})
|
||||||
remove-invalid-bonder-fees-routes
|
{:fx [(if swap?
|
||||||
remove-multichain-routes)
|
[:dispatch [:wallet/swap-proposal-error error-message]]
|
||||||
candidates-fix (comp ->old-route-paths
|
[:dispatch [:wallet/suggested-routes-error error-message]])]})
|
||||||
remove-invalid-bonder-fees-routes)
|
(let [best-routes-fix (comp ->old-route-paths
|
||||||
routes (-> data
|
remove-invalid-bonder-fees-routes
|
||||||
(data-store/rpc->suggested-routes)
|
remove-multichain-routes)
|
||||||
(update :best best-routes-fix)
|
candidates-fix (comp ->old-route-paths
|
||||||
(update :candidates candidates-fix))]
|
remove-invalid-bonder-fees-routes)
|
||||||
{:fx [[:dispatch [:wallet/suggested-routes-success routes]]]}))))
|
routes (-> data
|
||||||
|
(data-store/rpc->suggested-routes)
|
||||||
|
(update :best best-routes-fix)
|
||||||
|
(update :candidates candidates-fix))]
|
||||||
|
{:fx [(if swap?
|
||||||
|
[:dispatch [:wallet/swap-proposal-success routes]]
|
||||||
|
[:dispatch [:wallet/suggested-routes-success routes]])]})))))
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet/add-authorized-transaction
|
(rf/reg-event-fx :wallet/add-authorized-transaction
|
||||||
(fn [{:keys [db]} [transaction]]
|
(fn [{:keys [db]} [transaction]]
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
(ns status-im.contexts.wallet.swap.events
|
(ns status-im.contexts.wallet.swap.events
|
||||||
(:require [re-frame.core :as rf]
|
(:require [re-frame.core :as rf]
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
|
[status-im.contexts.wallet.send.utils :as send-utils]
|
||||||
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
|
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
[utils.number]))
|
[utils.number]))
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet.swap/start
|
(rf/reg-event-fx :wallet.swap/start
|
||||||
|
@ -14,7 +16,9 @@
|
||||||
(assoc-in [:wallet :ui :swap :asset-to-pay] token)
|
(assoc-in [:wallet :ui :swap :asset-to-pay] token)
|
||||||
(assoc-in [:wallet :ui :swap :network] network))
|
(assoc-in [:wallet :ui :swap :network] network))
|
||||||
:fx (if network
|
:fx (if network
|
||||||
[[:dispatch [:navigate-to :screen/wallet.swap-propasal]]
|
[[:dispatch
|
||||||
|
[:navigate-to-within-stack
|
||||||
|
[:screen/wallet.setup-swap :screen/wallet.swap-select-asset-to-pay]]]
|
||||||
[:dispatch [:wallet.swap/set-default-slippage]]]
|
[:dispatch [:wallet.swap/set-default-slippage]]]
|
||||||
[[:dispatch
|
[[:dispatch
|
||||||
[:show-bottom-sheet
|
[:show-bottom-sheet
|
||||||
|
@ -30,10 +34,6 @@
|
||||||
:stack-id
|
:stack-id
|
||||||
:screen/wallet.swap-select-asset-to-pay}]))}])}]]])}))
|
:screen/wallet.swap-select-asset-to-pay}]))}])}]]])}))
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet.swap/clean-asset-to-pay
|
|
||||||
(fn [{:keys [db]}]
|
|
||||||
{:db (update-in db [:wallet :ui :swap] dissoc :asset-to-pay)}))
|
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet.swap/set-default-slippage
|
(rf/reg-event-fx :wallet.swap/set-default-slippage
|
||||||
(fn [{:keys [db]}]
|
(fn [{:keys [db]}]
|
||||||
{:db
|
{:db
|
||||||
|
@ -47,18 +47,113 @@
|
||||||
(fn [{:keys [db]} [{:keys [token]}]]
|
(fn [{:keys [db]} [{:keys [token]}]]
|
||||||
{:db (assoc-in db [:wallet :ui :swap :asset-to-receive] token)}))
|
{:db (assoc-in db [:wallet :ui :swap :asset-to-receive] token)}))
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet.swap/set-pay-amount
|
|
||||||
(fn [{:keys [db]} [amount]]
|
|
||||||
{:db (assoc-in db [:wallet :ui :swap :pay-amount] amount)}))
|
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet.swap/set-swap-proposal
|
|
||||||
(fn [{:keys [db]} [swap-proposal]]
|
|
||||||
{:db (assoc-in db [:wallet :ui :swap :swap-proposal] swap-proposal)}))
|
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet.swap/set-provider
|
|
||||||
(fn [{:keys [db]}]
|
|
||||||
{:db (assoc-in db [:wallet :ui :swap :providers] [constants/swap-default-provider])}))
|
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet.swap/recalculate-fees
|
(rf/reg-event-fx :wallet.swap/recalculate-fees
|
||||||
(fn [{:keys [db]} [loading-fees?]]
|
(fn [{:keys [db]} [loading-fees?]]
|
||||||
{:db (assoc-in db [:wallet :ui :swap :loading-fees?] loading-fees?)}))
|
{:db (assoc-in db [:wallet :ui :swap :loading-fees?] loading-fees?)}))
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/start-get-swap-proposal
|
||||||
|
(fn [{:keys [db]} [{:keys [amount-in amount-out]}]]
|
||||||
|
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
|
||||||
|
{:keys [asset-to-pay asset-to-receive
|
||||||
|
network]} (get-in db [:wallet :ui :swap])
|
||||||
|
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||||
|
networks ((if test-networks-enabled? :test :prod)
|
||||||
|
(get-in db [:wallet :networks]))
|
||||||
|
network-chain-ids (map :chain-id networks)
|
||||||
|
pay-token-decimal (:decimals asset-to-pay)
|
||||||
|
pay-token-id (:symbol asset-to-pay)
|
||||||
|
receive-token-id (:symbol asset-to-receive)
|
||||||
|
receive-token-decimals (:decimals asset-to-receive)
|
||||||
|
gas-rates constants/gas-rate-medium
|
||||||
|
amount-in-hex (if amount-in
|
||||||
|
(send-utils/amount-in-hex amount-in pay-token-decimal)
|
||||||
|
0)
|
||||||
|
amount-out-hex (when amount-out
|
||||||
|
(send-utils/amount-in-hex amount-out receive-token-decimals))
|
||||||
|
to-address wallet-address
|
||||||
|
from-address wallet-address
|
||||||
|
swap-chain-id (:chain-id network)
|
||||||
|
disabled-to-chain-ids (filter #(not= % swap-chain-id) network-chain-ids)
|
||||||
|
disabled-from-chain-ids (filter #(not= % swap-chain-id) network-chain-ids)
|
||||||
|
from-locked-amount {}
|
||||||
|
send-type constants/send-type-swap
|
||||||
|
request-uuid (str (random-uuid))
|
||||||
|
params [(cond->
|
||||||
|
{:uuid request-uuid
|
||||||
|
:sendType send-type
|
||||||
|
:addrFrom from-address
|
||||||
|
:addrTo to-address
|
||||||
|
:tokenID pay-token-id
|
||||||
|
:toTokenID receive-token-id
|
||||||
|
:disabledFromChainIDs disabled-from-chain-ids
|
||||||
|
:disabledToChainIDs disabled-to-chain-ids
|
||||||
|
:gasFeeMode gas-rates
|
||||||
|
:fromLockedAmount from-locked-amount}
|
||||||
|
amount-in (assoc :amountIn amount-in-hex)
|
||||||
|
amount-out (assoc :amountOut amount-out-hex))]]
|
||||||
|
(when-let [amount (or amount-in amount-out)]
|
||||||
|
{:db (update-in db
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
#(-> %
|
||||||
|
(assoc
|
||||||
|
:last-request-uuid request-uuid
|
||||||
|
:amount amount
|
||||||
|
:loading-swap-proposal? true)
|
||||||
|
(dissoc :error-response)))
|
||||||
|
:json-rpc/call [{:method "wallet_getSuggestedRoutesV2Async"
|
||||||
|
:params params
|
||||||
|
:on-error (fn [error]
|
||||||
|
(rf/dispatch [:wallet/swap-proposal-error error])
|
||||||
|
(log/error "failed to get suggested routes (async)"
|
||||||
|
{:event :wallet/start-get-swap-proposal
|
||||||
|
:error (:message error)
|
||||||
|
:params params}))}]}))))
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/swap-proposal-success
|
||||||
|
(fn [{:keys [db]} [swap-proposal]]
|
||||||
|
(let [last-request-uuid (get-in db [:wallet :ui :swap :last-request-uuid])
|
||||||
|
request-uuid (:uuid swap-proposal)
|
||||||
|
best-routes (:best swap-proposal)
|
||||||
|
error-response (:error-response swap-proposal)]
|
||||||
|
(when (= request-uuid last-request-uuid)
|
||||||
|
{:db (update-in db
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
assoc
|
||||||
|
:swap-proposal (first best-routes)
|
||||||
|
:error-response (when (empty? best-routes) error-response)
|
||||||
|
:loading-swap-proposal? false)}))))
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/swap-proposal-error
|
||||||
|
(fn [{:keys [db]} [error-message]]
|
||||||
|
{:db (-> db
|
||||||
|
(update-in [:wallet :ui :swap] dissoc :route :swap-proposal)
|
||||||
|
(assoc-in [:wallet :ui :swap :loading-swap-proposal?] false)
|
||||||
|
(assoc-in [:wallet :ui :swap :error-response] error-message))
|
||||||
|
:fx [[:dispatch
|
||||||
|
[:toasts/upsert
|
||||||
|
{:id :swap-proposal-error
|
||||||
|
:type :negative
|
||||||
|
:text error-message}]]]}))
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/stop-get-swap-proposal
|
||||||
|
(fn []
|
||||||
|
{:json-rpc/call [{:method "wallet_stopSuggestedRoutesV2AsyncCalcualtion"
|
||||||
|
:params []
|
||||||
|
:on-error (fn [error]
|
||||||
|
(log/error "failed to stop fetching swap proposals"
|
||||||
|
{:event :wallet/stop-get-swap-proposal
|
||||||
|
:error error}))}]}))
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/clean-swap-proposal
|
||||||
|
(fn [{:keys [db]}]
|
||||||
|
{:db (update-in db
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
dissoc
|
||||||
|
:last-request-uuid
|
||||||
|
:swap-proposal
|
||||||
|
:error-response
|
||||||
|
:loading-swap-proposal?)}))
|
||||||
|
|
||||||
|
(rf/reg-event-fx :wallet/clean-swap
|
||||||
|
(fn [{:keys [db]}]
|
||||||
|
{:db (update-in db [:wallet :ui] dissoc :swap)}))
|
||||||
|
|
|
@ -18,22 +18,6 @@
|
||||||
:value search-text
|
:value search-text
|
||||||
:on-change-text on-change-text}]])
|
:on-change-text on-change-text}]])
|
||||||
|
|
||||||
(def dummy-swap-proposal
|
|
||||||
{:from {:chain-id 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}
|
|
||||||
:estimated-time 3
|
|
||||||
:receive-amount "99.98"
|
|
||||||
:pay-token {:symbol "SNT"
|
|
||||||
:address "0x432492384728934239789"}
|
|
||||||
:receive-token {:symbol "USDT"
|
|
||||||
:address "0x432492384728934239789"}})
|
|
||||||
|
|
||||||
(defn- assets-view
|
(defn- assets-view
|
||||||
[search-text on-change-text]
|
[search-text on-change-text]
|
||||||
(let [on-token-press (fn [token]
|
(let [on-token-press (fn [token]
|
||||||
|
@ -44,10 +28,8 @@
|
||||||
:network (when (= (count token-networks) 1)
|
:network (when (= (count token-networks) 1)
|
||||||
(first token-networks))
|
(first token-networks))
|
||||||
:stack-id :screen/wallet.swap-select-asset-to-pay}])
|
:stack-id :screen/wallet.swap-select-asset-to-pay}])
|
||||||
(rf/dispatch [:wallet.swap/select-asset-to-receive {:token asset-to-receive}])
|
(rf/dispatch [:wallet.swap/select-asset-to-receive
|
||||||
(rf/dispatch [:wallet.swap/set-pay-amount 100])
|
{:token asset-to-receive}])))]
|
||||||
(rf/dispatch [:wallet.swap/set-swap-proposal dummy-swap-proposal])
|
|
||||||
(rf/dispatch [:wallet.swap/set-provider])))]
|
|
||||||
[:<>
|
[:<>
|
||||||
[search-input search-text on-change-text]
|
[search-input search-text on-change-text]
|
||||||
[asset-list/view
|
[asset-list/view
|
||||||
|
@ -59,7 +41,7 @@
|
||||||
(let [[search-text set-search-text] (rn/use-state "")
|
(let [[search-text set-search-text] (rn/use-state "")
|
||||||
on-change-text #(set-search-text %)
|
on-change-text #(set-search-text %)
|
||||||
on-close (fn []
|
on-close (fn []
|
||||||
(rf/dispatch [:wallet.swap/clean-asset-to-pay])
|
(rf/dispatch [:wallet/clean-swap])
|
||||||
(rf/dispatch [:navigate-back]))]
|
(rf/dispatch [:navigate-back]))]
|
||||||
[rn/safe-area-view {:style style/container}
|
[rn/safe-area-view {:style style/container}
|
||||||
[account-switcher/view
|
[account-switcher/view
|
||||||
|
|
|
@ -22,14 +22,19 @@
|
||||||
:height 36
|
:height 36
|
||||||
:background-color :transparent})
|
:background-color :transparent})
|
||||||
|
|
||||||
(def swap-order-button
|
(defn swap-order-button
|
||||||
{:margin-top -9
|
[approval-required?]
|
||||||
|
{:margin-top (if approval-required? 3 -9)
|
||||||
:z-index 2
|
:z-index 2
|
||||||
:align-self :center})
|
:align-self :center})
|
||||||
|
|
||||||
(def receive-token-swap-input-container
|
(defn receive-token-swap-input-container
|
||||||
{:margin-top -9})
|
[approval-required?]
|
||||||
|
{:margin-top (if approval-required? 3 -9)})
|
||||||
|
|
||||||
(def footer-container
|
(def footer-container
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:justify-content :flex-end})
|
:justify-content :flex-end})
|
||||||
|
|
||||||
|
(def alert-banner
|
||||||
|
{:height 40})
|
||||||
|
|
|
@ -1,13 +1,35 @@
|
||||||
(ns status-im.contexts.wallet.swap.setup-swap.view
|
(ns status-im.contexts.wallet.swap.setup-swap.view
|
||||||
(:require [quo.core :as quo]
|
(:require [clojure.string :as string]
|
||||||
|
[native-module.core :as native-module]
|
||||||
|
[quo.core :as quo]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.safe-area :as safe-area]
|
||||||
|
[status-im.common.controlled-input.utils :as controlled-input]
|
||||||
[status-im.common.events-helper :as events-helper]
|
[status-im.common.events-helper :as events-helper]
|
||||||
|
[status-im.constants :as constants]
|
||||||
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
|
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
|
||||||
[status-im.contexts.wallet.common.utils :as utils]
|
[status-im.contexts.wallet.common.utils :as utils]
|
||||||
[status-im.contexts.wallet.swap.setup-swap.style :as style]
|
[status-im.contexts.wallet.swap.setup-swap.style :as style]
|
||||||
|
[utils.hex :as hex]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]))
|
[utils.money :as money]
|
||||||
|
[utils.number :as number]
|
||||||
|
[utils.re-frame :as rf]
|
||||||
|
[utils.string :as utils.string]))
|
||||||
|
|
||||||
|
(def ^:private min-token-decimals-to-display 6)
|
||||||
|
(def ^:private default-text-for-unfocused-input "0.00")
|
||||||
|
|
||||||
|
(defn- on-close
|
||||||
|
[]
|
||||||
|
(rf/dispatch [:wallet/clean-swap-proposal])
|
||||||
|
(events-helper/navigate-back))
|
||||||
|
|
||||||
|
(defn- fetch-swap-proposal
|
||||||
|
[{:keys [amount valid-input?]}]
|
||||||
|
(if valid-input?
|
||||||
|
(rf/dispatch [:wallet/start-get-swap-proposal {:amount-in amount}])
|
||||||
|
(rf/dispatch [:wallet/clean-swap-proposal])))
|
||||||
|
|
||||||
(defn- data-item
|
(defn- data-item
|
||||||
[{:keys [title subtitle size subtitle-icon loading?]}]
|
[{:keys [title subtitle size subtitle-icon loading?]}]
|
||||||
|
@ -23,110 +45,243 @@
|
||||||
:icon subtitle-icon}])
|
:icon subtitle-icon}])
|
||||||
|
|
||||||
(defn- transaction-details
|
(defn- transaction-details
|
||||||
[{:keys [max-slippage native-currency-symbol loading-fees?]}]
|
[]
|
||||||
(let [max-fees (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])]
|
(let [max-fees (rf/sub [:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||||
|
constants/token-for-fees-symbol])
|
||||||
|
max-slippage (rf/sub [:wallet/swap-max-slippage])
|
||||||
|
loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||||
|
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||||
|
loading? (or loading-fees? loading-swap-proposal?)]
|
||||||
[rn/view {:style style/details-container}
|
[rn/view {:style style/details-container}
|
||||||
[data-item
|
[data-item
|
||||||
{:title (i18n/label :t/max-fees)
|
{:title (i18n/label :t/max-fees)
|
||||||
:subtitle max-fees
|
:subtitle max-fees
|
||||||
:loading? loading-fees?
|
:loading? loading?
|
||||||
:size :small}]
|
:size :small}]
|
||||||
[data-item
|
[data-item
|
||||||
{:title (i18n/label :t/max-slippage)
|
{:title (i18n/label :t/max-slippage)
|
||||||
:subtitle max-slippage
|
:subtitle max-slippage
|
||||||
:subtitle-icon :i/edit
|
:subtitle-icon :i/edit
|
||||||
:loading? loading-fees?}]]))
|
:size :small
|
||||||
|
:loading? loading?}]]))
|
||||||
|
|
||||||
|
(defn- pay-token-input
|
||||||
|
[{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}]
|
||||||
|
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
|
||||||
|
network (rf/sub [:wallet/swap-network])
|
||||||
|
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||||
|
currency (rf/sub [:profile/currency])
|
||||||
|
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||||
|
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||||
|
approval-required (rf/sub [:wallet/swap-proposal-approval-required])
|
||||||
|
approval-amount-required (rf/sub [:wallet/swap-proposal-approval-amount-required])
|
||||||
|
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||||
|
pay-input-num-value (controlled-input/numeric-value input-state)
|
||||||
|
pay-input-amount (controlled-input/input-value input-state)
|
||||||
|
pay-token-symbol (:symbol asset-to-pay)
|
||||||
|
pay-token-decimals (:decimals asset-to-pay)
|
||||||
|
pay-token-balance-selected-chain (get-in asset-to-pay
|
||||||
|
[:balances-per-chain
|
||||||
|
(:chain-id network) :balance]
|
||||||
|
0)
|
||||||
|
pay-token-fiat-value (str
|
||||||
|
(utils/calculate-token-fiat-value
|
||||||
|
{:currency currency
|
||||||
|
:balance pay-input-num-value
|
||||||
|
:token asset-to-pay}))
|
||||||
|
available-crypto-limit (number/remove-trailing-zeroes
|
||||||
|
(.toFixed (money/bignumber
|
||||||
|
pay-token-balance-selected-chain)
|
||||||
|
(min pay-token-decimals
|
||||||
|
min-token-decimals-to-display)))
|
||||||
|
approval-amount-required-num (when approval-amount-required
|
||||||
|
(str (number/convert-to-whole-number
|
||||||
|
(native-module/hex-to-number
|
||||||
|
(hex/normalize-hex
|
||||||
|
approval-amount-required))
|
||||||
|
pay-token-decimals)))
|
||||||
|
pay-input-error? (and (not (string/blank? pay-input-amount))
|
||||||
|
(money/greater-than
|
||||||
|
(money/bignumber pay-input-num-value)
|
||||||
|
(money/bignumber
|
||||||
|
pay-token-balance-selected-chain)))
|
||||||
|
valid-pay-input? (and
|
||||||
|
(not (string/blank?
|
||||||
|
pay-input-amount))
|
||||||
|
(> pay-input-amount 0)
|
||||||
|
(not pay-input-error?))
|
||||||
|
request-fetch-swap-proposal (rn/use-callback
|
||||||
|
(fn []
|
||||||
|
(fetch-swap-proposal
|
||||||
|
{:amount pay-input-amount
|
||||||
|
:valid-input? valid-pay-input?}))
|
||||||
|
[pay-input-amount])]
|
||||||
|
(rn/use-effect
|
||||||
|
(fn []
|
||||||
|
(request-fetch-swap-proposal))
|
||||||
|
[pay-input-amount])
|
||||||
|
[quo/swap-input
|
||||||
|
{:type :pay
|
||||||
|
:error? pay-input-error?
|
||||||
|
:token pay-token-symbol
|
||||||
|
:customization-color :blue
|
||||||
|
:show-approval-label? (and swap-proposal approval-required)
|
||||||
|
:auto-focus? true
|
||||||
|
:status (cond
|
||||||
|
(and loading-swap-proposal? (not input-focused?)) :loading
|
||||||
|
input-focused? :typing
|
||||||
|
:else :disabled)
|
||||||
|
:currency-symbol currency-symbol
|
||||||
|
:on-token-press on-token-press
|
||||||
|
:on-max-press on-max-press
|
||||||
|
:on-input-focus on-input-focus
|
||||||
|
:value pay-input-amount
|
||||||
|
:fiat-value pay-token-fiat-value
|
||||||
|
:network-tag-props {:title (i18n/label :t/max-token
|
||||||
|
{:number available-crypto-limit
|
||||||
|
:token-symbol pay-token-symbol})
|
||||||
|
:networks [{:source (:source network)}]}
|
||||||
|
:approval-label-props {:status :approve
|
||||||
|
:token-value approval-amount-required-num
|
||||||
|
:button-props {:on-press on-approve-press}
|
||||||
|
:customization-color account-color
|
||||||
|
:token-symbol pay-token-symbol}}]))
|
||||||
|
|
||||||
|
(defn- swap-order-button
|
||||||
|
[{:keys [on-press]}]
|
||||||
|
(let [approval-required? (rf/sub [:wallet/swap-proposal-approval-required])]
|
||||||
|
[quo/swap-order-button
|
||||||
|
{:container-style (style/swap-order-button approval-required?)
|
||||||
|
:on-press on-press}]))
|
||||||
|
|
||||||
|
(defn- receive-token-input
|
||||||
|
[{:keys [on-input-focus on-token-press input-focused?]}]
|
||||||
|
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
|
||||||
|
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
|
||||||
|
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||||
|
currency (rf/sub [:profile/currency])
|
||||||
|
currency-symbol (rf/sub [:profile/currency-symbol])
|
||||||
|
amount-out (rf/sub [:wallet/swap-proposal-amount-out])
|
||||||
|
approval-required? (rf/sub [:wallet/swap-proposal-approval-required])
|
||||||
|
receive-token-symbol (:symbol asset-to-receive)
|
||||||
|
receive-token-decimals (:decimals asset-to-receive)
|
||||||
|
amount-out-whole-number (when amount-out
|
||||||
|
(number/convert-to-whole-number
|
||||||
|
(native-module/hex-to-number
|
||||||
|
(utils.hex/normalize-hex
|
||||||
|
amount-out))
|
||||||
|
receive-token-decimals))
|
||||||
|
amount-out-num (if amount-out-whole-number
|
||||||
|
(str amount-out-whole-number)
|
||||||
|
default-text-for-unfocused-input)
|
||||||
|
receive-token-fiat-value (str (utils/calculate-token-fiat-value
|
||||||
|
{:currency currency
|
||||||
|
:balance (or amount-out-whole-number 0)
|
||||||
|
:token asset-to-receive}))]
|
||||||
|
[quo/swap-input
|
||||||
|
{:type :receive
|
||||||
|
:error? false
|
||||||
|
:token receive-token-symbol
|
||||||
|
:customization-color account-color
|
||||||
|
:show-approval-label? false
|
||||||
|
:enable-swap? true
|
||||||
|
:input-disabled? true
|
||||||
|
:status (cond
|
||||||
|
(and loading-swap-proposal? (not input-focused?)) :loading
|
||||||
|
input-focused? :typing
|
||||||
|
:else :disabled)
|
||||||
|
:currency-symbol currency-symbol
|
||||||
|
:on-token-press on-token-press
|
||||||
|
:on-input-focus on-input-focus
|
||||||
|
:value amount-out-num
|
||||||
|
:fiat-value receive-token-fiat-value
|
||||||
|
:container-style (style/receive-token-swap-input-container approval-required?)}]))
|
||||||
|
|
||||||
|
(defn- action-button
|
||||||
|
[{:keys [on-press]}]
|
||||||
|
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
|
||||||
|
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||||
|
loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||||
|
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||||
|
approval-required? (rf/sub [:wallet/swap-proposal-approval-required])]
|
||||||
|
[quo/bottom-actions
|
||||||
|
{:actions :one-action
|
||||||
|
:button-one-label (i18n/label :t/review-swap)
|
||||||
|
:button-one-props {:disabled? (or (not swap-proposal)
|
||||||
|
approval-required?
|
||||||
|
loading-swap-proposal?
|
||||||
|
loading-fees?)
|
||||||
|
:customization-color account-color
|
||||||
|
:on-press on-press}}]))
|
||||||
|
|
||||||
(defn view
|
(defn view
|
||||||
[]
|
[]
|
||||||
(let [[pay-value set-pay-value] (rn/use-state "")
|
(let [[pay-input-state set-pay-input-state] (rn/use-state controlled-input/init-state)
|
||||||
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
|
[pay-input-focused? set-pay-input-focused?] (rn/use-state true)
|
||||||
{:keys [max-slippage swap-proposal loading-fees?
|
error-response (rf/sub [:wallet/swap-error-response])
|
||||||
receive-amount network]} (rf/sub [:wallet/swap])
|
network (rf/sub [:wallet/swap-network])
|
||||||
currency (rf/sub [:profile/currency])
|
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||||
currency-symbol (rf/sub [:profile/currency-symbol])
|
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||||
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||||
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
|
pay-input-amount (controlled-input/input-value pay-input-state)
|
||||||
|
pay-token-decimals (:decimals asset-to-pay)
|
||||||
pay-token-fiat-value (utils/calculate-token-fiat-value
|
network-chain-id (:chain-id network)
|
||||||
{:currency currency
|
pay-token-balance-selected-chain (get-in asset-to-pay
|
||||||
:balance (or pay-value 0)
|
[:balances-per-chain network-chain-id
|
||||||
:token asset-to-pay})
|
:balance]
|
||||||
receive-token-fiat-value (utils/calculate-token-fiat-value
|
0)
|
||||||
{:currency currency
|
on-press (rn/use-callback
|
||||||
:balance (or receive-amount 0)
|
(fn [c]
|
||||||
:token asset-to-receive})
|
(let
|
||||||
native-currency-symbol (get-in swap-proposal
|
[new-text (str pay-input-amount c)
|
||||||
[:from :native-currency-symbol])
|
valid-amount?
|
||||||
pay-token-symbol (:symbol asset-to-pay)
|
(utils.string/valid-amount-for-token-decimals?
|
||||||
receive-token-symbol (:symbol asset-to-receive)
|
pay-token-decimals
|
||||||
on-press (fn [v] (set-pay-value (str pay-value v)))
|
new-text)]
|
||||||
delete (fn []
|
(when valid-amount?
|
||||||
(set-pay-value #(subs % 0 (dec (count %)))))]
|
(set-pay-input-state
|
||||||
|
#(controlled-input/add-character % c))))))
|
||||||
|
on-long-press (rn/use-callback
|
||||||
|
(fn []
|
||||||
|
(set-pay-input-state controlled-input/delete-all)
|
||||||
|
(rf/dispatch [:wallet/clean-suggested-routes])))
|
||||||
|
delete (rn/use-callback
|
||||||
|
(fn []
|
||||||
|
(set-pay-input-state
|
||||||
|
controlled-input/delete-last)
|
||||||
|
(rf/dispatch [:wallet/clean-swap-proposal])))]
|
||||||
[rn/view {:style style/container}
|
[rn/view {:style style/container}
|
||||||
[account-switcher/view
|
[account-switcher/view
|
||||||
{:on-press events-helper/navigate-back
|
{:on-press on-close
|
||||||
:icon-name :i/arrow-left
|
:icon-name :i/arrow-left
|
||||||
:margin-top (safe-area/get-top)
|
:margin-top (safe-area/get-top)
|
||||||
:switcher-type :select-account}]
|
:switcher-type :select-account}]
|
||||||
[rn/view {:style style/inputs-container}
|
[rn/view {:style style/inputs-container}
|
||||||
[quo/swap-input
|
[pay-token-input
|
||||||
{:type :pay
|
{:input-state pay-input-state
|
||||||
:error? false
|
:on-max-press #(set-pay-input-state pay-token-balance-selected-chain)
|
||||||
:token pay-token-symbol
|
:input-focused? pay-input-focused?
|
||||||
:customization-color :blue
|
:on-token-press #(js/alert "Token Pressed")
|
||||||
:show-approval-label? false
|
:on-approve-press #(js/alert "Approve Pressed")
|
||||||
:status :default
|
:on-input-focus #(set-pay-input-focused? true)}]
|
||||||
:currency-symbol currency-symbol
|
[swap-order-button {:on-press #(js/alert "Swap Order Pressed")}]
|
||||||
:on-swap-press #(js/alert "Swap Pressed")
|
[receive-token-input
|
||||||
:on-token-press #(js/alert "Token Pressed")
|
{:input-focused? (not pay-input-focused?)
|
||||||
:on-max-press #(js/alert "Max Pressed")
|
:on-token-press #(js/alert "Token Pressed")
|
||||||
:value pay-value
|
:on-input-focus #(set-pay-input-focused? false)}]]
|
||||||
:fiat-value pay-token-fiat-value
|
|
||||||
:network-tag-props {:title (i18n/label :t/max-token
|
|
||||||
{:number 200
|
|
||||||
:token-symbol pay-token-symbol})
|
|
||||||
:networks [{:source (:source network)}]}
|
|
||||||
:approval-label-props {:status :approve
|
|
||||||
:token-value pay-value
|
|
||||||
:button-props {:on-press
|
|
||||||
#(js/alert "Approve Pressed")}
|
|
||||||
:customization-color color
|
|
||||||
:token-symbol pay-token-symbol}}]
|
|
||||||
[quo/swap-order-button
|
|
||||||
{:container-style style/swap-order-button
|
|
||||||
:on-press #(js/alert "Pressed")}]
|
|
||||||
[quo/swap-input
|
|
||||||
{:type :receive
|
|
||||||
:error? false
|
|
||||||
:token receive-token-symbol
|
|
||||||
:customization-color color
|
|
||||||
:show-approval-label? false
|
|
||||||
:enable-swap? true
|
|
||||||
:status :default
|
|
||||||
:currency-symbol currency-symbol
|
|
||||||
:on-swap-press #(js/alert "Swap Pressed")
|
|
||||||
:on-token-press #(js/alert "Token Pressed")
|
|
||||||
:on-max-press #(js/alert "Max Pressed")
|
|
||||||
:value receive-amount
|
|
||||||
:fiat-value receive-token-fiat-value
|
|
||||||
:container-style style/receive-token-swap-input-container}]]
|
|
||||||
[rn/view {:style style/footer-container}
|
[rn/view {:style style/footer-container}
|
||||||
(when swap-proposal
|
(when error-response
|
||||||
[transaction-details
|
[quo/alert-banner
|
||||||
{:native-currency-symbol native-currency-symbol
|
{:container-style style/alert-banner
|
||||||
:max-slippage max-slippage
|
:text (i18n/label :t/something-went-wrong-please-try-again-later)}])
|
||||||
:loading-fees? loading-fees?}])
|
(when (or loading-swap-proposal? swap-proposal)
|
||||||
[quo/bottom-actions
|
[transaction-details])
|
||||||
{:actions :one-action
|
[action-button
|
||||||
:button-one-label (i18n/label :t/review-swap)
|
{:on-press #(js/alert "Review swap pressed")}]]
|
||||||
:button-one-props {:disabled? (or (not swap-proposal)
|
|
||||||
loading-fees?)
|
|
||||||
:customization-color color
|
|
||||||
:on-press #(js/alert "Review swap pressed")}}]]
|
|
||||||
[quo/numbered-keyboard
|
[quo/numbered-keyboard
|
||||||
{:container-style style/keyboard-container
|
{:container-style style/keyboard-container
|
||||||
:left-action :dot
|
:left-action :dot
|
||||||
:delete-key? true
|
:delete-key? true
|
||||||
:on-press on-press
|
:on-press on-press
|
||||||
:on-delete delete}]]))
|
:on-delete delete
|
||||||
|
:on-long-press on-long-press}]]))
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
(ns status-im.subs.wallet.swap
|
(ns status-im.subs.wallet.swap
|
||||||
(:require [re-frame.core :as rf]
|
(:require [clojure.string :as string]
|
||||||
|
[re-frame.core :as rf]
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.contexts.wallet.common.utils :as utils]
|
[status-im.contexts.wallet.common.utils :as utils]
|
||||||
|
[status-im.contexts.wallet.send.utils :as send-utils]
|
||||||
[utils.money :as money]))
|
[utils.money :as money]))
|
||||||
|
|
||||||
(rf/reg-sub
|
(rf/reg-sub
|
||||||
|
@ -19,6 +21,16 @@
|
||||||
:<- [:wallet/swap]
|
:<- [:wallet/swap]
|
||||||
:-> :asset-to-receive)
|
:-> :asset-to-receive)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-network
|
||||||
|
:<- [:wallet/swap]
|
||||||
|
:-> :network)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-error-response
|
||||||
|
:<- [:wallet/swap]
|
||||||
|
:-> :error-response)
|
||||||
|
|
||||||
(rf/reg-sub
|
(rf/reg-sub
|
||||||
:wallet/swap-asset-to-pay-token-symbol
|
:wallet/swap-asset-to-pay-token-symbol
|
||||||
:<- [:wallet/swap-asset-to-pay]
|
:<- [:wallet/swap-asset-to-pay]
|
||||||
|
@ -62,3 +74,59 @@
|
||||||
:wallet/swap-max-slippage
|
:wallet/swap-max-slippage
|
||||||
:<- [:wallet/swap]
|
:<- [:wallet/swap]
|
||||||
:-> :max-slippage)
|
:-> :max-slippage)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-loading-fees?
|
||||||
|
:<- [:wallet/swap]
|
||||||
|
:-> :loading-fees?)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-proposal
|
||||||
|
:<- [:wallet/swap]
|
||||||
|
:-> :swap-proposal)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-loading-swap-proposal?
|
||||||
|
:<- [:wallet/swap]
|
||||||
|
:-> :loading-swap-proposal?)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-proposal-amount-out
|
||||||
|
:<- [:wallet/swap-proposal]
|
||||||
|
:-> :amount-out)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-proposal-approval-required
|
||||||
|
:<- [:wallet/swap-proposal]
|
||||||
|
:-> :approval-required)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/swap-proposal-approval-amount-required
|
||||||
|
:<- [:wallet/swap-proposal]
|
||||||
|
:-> :approval-amount-required)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||||
|
:<- [:wallet/current-viewing-account]
|
||||||
|
:<- [:wallet/swap-proposal]
|
||||||
|
:<- [:profile/currency]
|
||||||
|
:<- [:profile/currency-symbol]
|
||||||
|
(fn [[account swap-proposal currency currency-symbol] [_ token-symbol-for-fees]]
|
||||||
|
(when token-symbol-for-fees
|
||||||
|
(let [tokens (:tokens account)
|
||||||
|
token-for-fees (first (filter #(= (string/lower-case (:symbol %))
|
||||||
|
(string/lower-case token-symbol-for-fees))
|
||||||
|
tokens))
|
||||||
|
fee-in-native-token (send-utils/calculate-full-route-gas-fee [swap-proposal])
|
||||||
|
fee-in-crypto-formatted (utils/get-standard-crypto-format
|
||||||
|
token-for-fees
|
||||||
|
fee-in-native-token)
|
||||||
|
fee-in-fiat (utils/calculate-token-fiat-value
|
||||||
|
{:currency currency
|
||||||
|
:balance fee-in-native-token
|
||||||
|
:token token-for-fees})
|
||||||
|
fee-formatted (utils/get-standard-fiat-format
|
||||||
|
fee-in-crypto-formatted
|
||||||
|
currency-symbol
|
||||||
|
fee-in-fiat)]
|
||||||
|
fee-formatted))))
|
||||||
|
|
|
@ -16,6 +16,27 @@
|
||||||
:popular? true
|
:popular? true
|
||||||
:token? false}})
|
:token? false}})
|
||||||
|
|
||||||
|
(def ^:private accounts-with-tokens
|
||||||
|
{:0x1 {:tokens [{:symbol "ETH"
|
||||||
|
:balances-per-chain {1 {:raw-balance "100"}}
|
||||||
|
:market-values-per-currency {:usd {:price 10000}}}
|
||||||
|
{:symbol "SNT"
|
||||||
|
:balances-per-chain {1 {:raw-balance "100"}}
|
||||||
|
:market-values-per-currency {:usd {:price 10000}}}]
|
||||||
|
:network-preferences-names #{}
|
||||||
|
:customization-color nil
|
||||||
|
:operable? true
|
||||||
|
:operable :fully
|
||||||
|
:address "0x1"}
|
||||||
|
:0x2 {:tokens [{:symbol "SNT"
|
||||||
|
:balances-per-chain {1 {:raw-balance "200"}}
|
||||||
|
:market-values-per-currency {:usd {:price 10000}}}]
|
||||||
|
:network-preferences-names #{}
|
||||||
|
:customization-color nil
|
||||||
|
:operable? true
|
||||||
|
:operable :partially
|
||||||
|
:address "0x2"}})
|
||||||
|
|
||||||
(def networks
|
(def networks
|
||||||
{:mainnet-network
|
{:mainnet-network
|
||||||
{:full-name "Mainnet"
|
{:full-name "Mainnet"
|
||||||
|
@ -110,7 +131,19 @@
|
||||||
:token-list-id ""
|
:token-list-id ""
|
||||||
:built-on "ETH"
|
:built-on "ETH"
|
||||||
:verified true}
|
:verified true}
|
||||||
:network nil})
|
:network (networks :mainnet-network)
|
||||||
|
:swap-proposal {:amount-out "0x10000"
|
||||||
|
:amount-in "0x10000"
|
||||||
|
:approval-required true
|
||||||
|
:approval-amount-required "0x10000"
|
||||||
|
:gas-amount "25000"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "4"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "0"}}
|
||||||
|
:error-response "Error"
|
||||||
|
:loading-fees? false
|
||||||
|
:loading-swap-proposal? false
|
||||||
|
:max-slippage 0.5})
|
||||||
|
|
||||||
(h/deftest-sub :wallet/swap
|
(h/deftest-sub :wallet/swap
|
||||||
[sub-name]
|
[sub-name]
|
||||||
|
@ -160,3 +193,91 @@
|
||||||
(assoc :currencies currencies)
|
(assoc :currencies currencies)
|
||||||
(assoc-in [:wallet :ui :swap] swap-data)))
|
(assoc-in [:wallet :ui :swap] swap-data)))
|
||||||
(is (match? {:crypto "1 SNT" :fiat "$0.03"} (rf/sub [sub-name 1])))))
|
(is (match? {:crypto "1 SNT" :fiat "$0.03"} (rf/sub [sub-name 1])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-network
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return the current swap network"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (match? (swap-data :network) (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-error-response
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return the swap error response"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (match? (swap-data :error-response) (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-max-slippage
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return the max slippage for the swap"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (match? 0.5 (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-loading-fees?
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return if swap is loading fees"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (false? (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-loading-swap-proposal?
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return if swap is loading the swap proposal"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (false? (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-proposal
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return the swap proposal"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (match? (swap-data :swap-proposal) (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-proposal-amount-out
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return the amount out in the swap proposal"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (match? "0x10000" (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-proposal-approval-required
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return if approval is required in the swap proposal"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (true? (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/swap-proposal-approval-amount-required
|
||||||
|
[sub-name]
|
||||||
|
(testing "Return the approval amount required in the swap proposal"
|
||||||
|
(swap! rf-db/app-db assoc-in
|
||||||
|
[:wallet :ui :swap]
|
||||||
|
swap-data)
|
||||||
|
(is (match? "0x10000" (rf/sub [sub-name])))))
|
||||||
|
|
||||||
|
(h/deftest-sub :wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||||
|
[sub-name]
|
||||||
|
(testing "wallet send fee calculated and formatted in fiat"
|
||||||
|
(swap! rf-db/app-db
|
||||||
|
#(-> %
|
||||||
|
(assoc-in [:wallet :accounts] accounts-with-tokens)
|
||||||
|
(assoc-in [:wallet :current-viewing-account-address] "0x1")
|
||||||
|
(assoc-in [:wallet :ui :swap] swap-data)
|
||||||
|
(assoc-in [:currencies] currencies)
|
||||||
|
(assoc-in [:profile/profile :currency] :usd)
|
||||||
|
(assoc-in [:profile/profile :currency-symbol] "$")))
|
||||||
|
|
||||||
|
(let [token-symbol-for-fees "ETH"
|
||||||
|
result (rf/sub [sub-name token-symbol-for-fees])]
|
||||||
|
(is (match? result "$1.00")))))
|
||||||
|
|
|
@ -202,11 +202,6 @@
|
||||||
[gas gas-price]
|
[gas gas-price]
|
||||||
(.times ^js (bignumber gas) ^js (bignumber gas-price)))
|
(.times ^js (bignumber gas) ^js (bignumber gas-price)))
|
||||||
|
|
||||||
(defn crypto->fiat
|
|
||||||
[crypto fiat-price]
|
|
||||||
(when-let [^js bn (bignumber crypto)]
|
|
||||||
(.times bn ^js (bignumber fiat-price))))
|
|
||||||
|
|
||||||
(defn percent-change
|
(defn percent-change
|
||||||
[from to]
|
[from to]
|
||||||
(let [^js bnf (bignumber from)
|
(let [^js bnf (bignumber from)
|
||||||
|
@ -221,6 +216,12 @@
|
||||||
(when-let [^js bn (bignumber n)]
|
(when-let [^js bn (bignumber n)]
|
||||||
(.round bn decimals)))
|
(.round bn decimals)))
|
||||||
|
|
||||||
|
(defn crypto->fiat
|
||||||
|
[crypto fiat-price]
|
||||||
|
(when-let [^js bn (bignumber crypto)]
|
||||||
|
(-> (.times bn ^js (bignumber fiat-price))
|
||||||
|
(with-precision 2))))
|
||||||
|
|
||||||
(defn sufficient-funds?
|
(defn sufficient-funds?
|
||||||
[^js amount ^js balance]
|
[^js amount ^js balance]
|
||||||
(when (and amount balance)
|
(when (and amount balance)
|
||||||
|
|
|
@ -15,6 +15,17 @@
|
||||||
(/ (Math/round (* n scale))
|
(/ (Math/round (* n scale))
|
||||||
scale)))
|
scale)))
|
||||||
|
|
||||||
|
(defn convert-to-whole-number
|
||||||
|
"Converts a fractional `amount` to its corresponding whole number representation
|
||||||
|
by dividing it by 10 raised to the power of `decimals`. This is often used in financial
|
||||||
|
calculations where amounts are stored in their smallest units (e.g., cents) and need
|
||||||
|
to be converted to their whole number equivalents (e.g., dollars).
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
(convert-to-whole-number 12345 2) ; => 123.45"
|
||||||
|
[amount decimals]
|
||||||
|
(/ amount (Math/pow 10 decimals)))
|
||||||
|
|
||||||
(defn parse-int
|
(defn parse-int
|
||||||
"Parses `n` as an integer. Defaults to zero or `default` instead of NaN."
|
"Parses `n` as an integer. Defaults to zero or `default` instead of NaN."
|
||||||
([n]
|
([n]
|
||||||
|
|
|
@ -3,6 +3,26 @@
|
||||||
[cljs.test :refer [deftest is testing]]
|
[cljs.test :refer [deftest is testing]]
|
||||||
[utils.number]))
|
[utils.number]))
|
||||||
|
|
||||||
|
(deftest convert-to-whole-number-test
|
||||||
|
(testing "correctly converts fractional amounts to whole numbers"
|
||||||
|
(is (= 123.45 (utils.number/convert-to-whole-number 12345 2)))
|
||||||
|
(is (= 1.2345 (utils.number/convert-to-whole-number 12345 4)))
|
||||||
|
(is (= 12345.0 (utils.number/convert-to-whole-number 1234500 2)))
|
||||||
|
(is (= 0.123 (utils.number/convert-to-whole-number 123 3)))
|
||||||
|
(is (= 1000.0 (utils.number/convert-to-whole-number 1000000 3))))
|
||||||
|
|
||||||
|
(testing "handles zero decimals"
|
||||||
|
(is (= 12345 (utils.number/convert-to-whole-number 12345 0))))
|
||||||
|
|
||||||
|
(testing "handles negative amounts"
|
||||||
|
(is (= -123.45 (utils.number/convert-to-whole-number -12345 2)))
|
||||||
|
(is (= -1.2345 (utils.number/convert-to-whole-number -12345 4)))
|
||||||
|
(is (= -0.123 (utils.number/convert-to-whole-number -123 3))))
|
||||||
|
|
||||||
|
(testing "handles zero amount"
|
||||||
|
(is (= 0 (utils.number/convert-to-whole-number 0 2)))
|
||||||
|
(is (= 0 (utils.number/convert-to-whole-number 0 0)))))
|
||||||
|
|
||||||
(deftest parse-int-test
|
(deftest parse-int-test
|
||||||
(testing "defaults to zero"
|
(testing "defaults to zero"
|
||||||
(is (= 0 (utils.number/parse-int nil))))
|
(is (= 0 (utils.number/parse-int nil))))
|
||||||
|
|
|
@ -88,3 +88,9 @@
|
||||||
[url]
|
[url]
|
||||||
(when (string? url)
|
(when (string? url)
|
||||||
(string/replace url #"^https?://" "")))
|
(string/replace url #"^https?://" "")))
|
||||||
|
|
||||||
|
(defn valid-amount-for-token-decimals?
|
||||||
|
[token-decimals amount-text]
|
||||||
|
(let [regex-pattern (str "^\\d*\\.?\\d{0," token-decimals "}$")
|
||||||
|
regex (re-pattern regex-pattern)]
|
||||||
|
(re-matches regex amount-text)))
|
||||||
|
|
|
@ -2302,6 +2302,7 @@
|
||||||
"slow": "Slow",
|
"slow": "Slow",
|
||||||
"something-about-you": "Something about you",
|
"something-about-you": "Something about you",
|
||||||
"something-went-wrong": "Something went wrong",
|
"something-went-wrong": "Something went wrong",
|
||||||
|
"something-went-wrong-please-try-again-later": "Something went wrong, please try again later",
|
||||||
"soon": "Soon",
|
"soon": "Soon",
|
||||||
"sort-communities": "Sort communities",
|
"sort-communities": "Sort communities",
|
||||||
"special-characters": "Special characters",
|
"special-characters": "Special characters",
|
||||||
|
|
Loading…
Reference in New Issue