feat: autorefresh swap proposal (#21143)
Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
parent
cf5a395b29
commit
337ec963b7
|
@ -608,3 +608,4 @@
|
|||
(def ^:const transaction-status-failed "Failed")
|
||||
|
||||
(def ^:const min-token-decimals-to-display 6)
|
||||
(def ^:const swap-proposal-refresh-interval-ms 15000)
|
||||
|
|
|
@ -54,10 +54,6 @@
|
|||
(fn [{:keys [db]} [{:keys [token]}]]
|
||||
{:db (assoc-in db [:wallet :ui :swap :asset-to-receive] token)}))
|
||||
|
||||
(rf/reg-event-fx :wallet.swap/recalculate-fees
|
||||
(fn [{:keys [db]} [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])
|
||||
|
@ -120,16 +116,25 @@
|
|||
(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])
|
||||
view-id (:view-id db)
|
||||
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)}))))
|
||||
(cond-> {: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)}
|
||||
;; Router is unstable and it can return a swap proposal and after auto-refetching it can
|
||||
;; return an error. Ideally this shouldn't happen, but adding this behavior so if the
|
||||
;; user is in swap confirmation screen or in token approval confirmation screen, we
|
||||
;; navigate back to setup swap screen so proper error is displayed.
|
||||
(and (empty? best-routes) (= view-id :screen/wallet.swap-set-spending-cap))
|
||||
(assoc :fx [[:dismiss-modal :screen/wallet.swap-set-spending-cap]])
|
||||
(and (empty? best-routes) (= view-id :screen/wallet.swap-confirmation))
|
||||
(assoc :fx [[:navigate-back]]))))))
|
||||
|
||||
(rf/reg-event-fx :wallet/swap-proposal-error
|
||||
(fn [{:keys [db]} [error-message]]
|
||||
|
|
|
@ -73,11 +73,12 @@
|
|||
:style (style/section-label theme)
|
||||
:accessibility-label :spending-cap-label}
|
||||
(i18n/label :t/spending-cap)]
|
||||
[quo/approval-info
|
||||
{:type :spending-cap
|
||||
:unlimited-icon? false
|
||||
:label (str pay-amount " " pay-token-symbol)
|
||||
:avatar-props {:token pay-token-symbol}}]]))
|
||||
(when (and asset-to-pay pay-amount)
|
||||
[quo/approval-info
|
||||
{:type :spending-cap
|
||||
:unlimited-icon? false
|
||||
:label (str pay-amount " " pay-token-symbol)
|
||||
:avatar-props {:token pay-token-symbol}}])]))
|
||||
|
||||
(defn- account-section
|
||||
[]
|
||||
|
@ -93,14 +94,15 @@
|
|||
:style (style/section-label theme)
|
||||
:accessibility-label :account-label}
|
||||
(i18n/label :t/account)]
|
||||
[quo/approval-info
|
||||
{:type :account
|
||||
:unlimited-icon? false
|
||||
:label (:name account)
|
||||
:description (address-utils/get-short-wallet-address (:address account))
|
||||
:tag-label (str pay-amount " " pay-token-symbol)
|
||||
:avatar-props {:emoji (:emoji account)
|
||||
:customization-color (:color account)}}]]))
|
||||
(when (and asset-to-pay pay-amount)
|
||||
[quo/approval-info
|
||||
{:type :account
|
||||
:unlimited-icon? false
|
||||
:label (:name account)
|
||||
:description (address-utils/get-short-wallet-address (:address account))
|
||||
:tag-label (str pay-amount " " pay-token-symbol)
|
||||
:avatar-props {:emoji (:emoji account)
|
||||
:customization-color (:color account)}}])]))
|
||||
|
||||
(defn- on-option-press
|
||||
[{:keys [chain-id contract-address]}]
|
||||
|
@ -133,15 +135,16 @@
|
|||
:style (style/section-label theme)
|
||||
:accessibility-label :token-label}
|
||||
(i18n/label :t/token)]
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address pay-token-address})
|
||||
:unlimited-icon? false
|
||||
:label pay-token-symbol
|
||||
:description (address-utils/get-short-wallet-address pay-token-address)
|
||||
:avatar-props {:token pay-token-symbol}}]]))
|
||||
(when asset-to-pay
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address pay-token-address})
|
||||
:unlimited-icon? false
|
||||
:label pay-token-symbol
|
||||
:description (address-utils/get-short-wallet-address pay-token-address)
|
||||
:avatar-props {:token pay-token-symbol}}])]))
|
||||
|
||||
(defn- spender-contract-section
|
||||
[]
|
||||
|
@ -156,15 +159,16 @@
|
|||
:style (style/section-label theme)
|
||||
:accessibility-label :spender-contract-label}
|
||||
(i18n/label :t/spender-contract)]
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address (:contract-address provider)})
|
||||
:unlimited-icon? false
|
||||
:label (:full-name provider)
|
||||
:description (address-utils/get-short-wallet-address (:contract-address provider))
|
||||
:avatar-props {:image (resources/get-network (:name provider))}}]]))
|
||||
(when provider
|
||||
[quo/approval-info
|
||||
{:type :token-contract
|
||||
:option-icon :i/options
|
||||
:on-option-press #(on-option-press {:chain-id network-chain-id
|
||||
:contract-address (:contract-address provider)})
|
||||
:unlimited-icon? false
|
||||
:label (:full-name provider)
|
||||
:description (address-utils/get-short-wallet-address (:contract-address provider))
|
||||
:avatar-props {:image (resources/get-network (:name provider))}}])]))
|
||||
|
||||
(defn- data-item
|
||||
[{:keys [network-image title subtitle size loading?]}]
|
||||
|
@ -181,11 +185,11 @@
|
|||
|
||||
(defn- transaction-details
|
||||
[]
|
||||
(let [network (rf/sub [:wallet/swap-network])
|
||||
max-fees (rf/sub [:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||
constants/token-for-fees-symbol])
|
||||
loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
estimated-time (rf/sub [:wallet/swap-proposal-estimated-time])]
|
||||
(let [network (rf/sub [:wallet/swap-network])
|
||||
max-fees (rf/sub [:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||
constants/token-for-fees-symbol])
|
||||
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||
estimated-time (rf/sub [:wallet/swap-proposal-estimated-time])]
|
||||
[rn/view {:style style/details-container}
|
||||
[:<>
|
||||
[data-item
|
||||
|
@ -194,26 +198,31 @@
|
|||
:network-image (:source network)}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-fees)
|
||||
:subtitle max-fees
|
||||
:loading? loading-fees?
|
||||
:subtitle (if (and estimated-time max-fees) max-fees (i18n/label :t/unknown))
|
||||
:loading? loading-swap-proposal?
|
||||
:size :small}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/est-time)
|
||||
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time)})}]]]))
|
||||
:subtitle (if estimated-time
|
||||
(i18n/label :t/time-in-mins {:minutes (str estimated-time)})
|
||||
(i18n/label :t/unknown))
|
||||
:loading? loading-swap-proposal?
|
||||
:size :small}]]]))
|
||||
|
||||
(defn- slide-button
|
||||
[]
|
||||
(let [loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
on-auth-success (rn/use-callback #(rf/dispatch
|
||||
[:wallet/swap-transaction
|
||||
(security/safe-unmask-data %)]))]
|
||||
(let [loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
on-auth-success (rn/use-callback #(rf/dispatch
|
||||
[:wallet/swap-transaction
|
||||
(security/safe-unmask-data %)]))]
|
||||
[standard-auth/slide-button
|
||||
{:size :size-48
|
||||
:track-text (i18n/label :t/slide-to-sign)
|
||||
:container-style {:z-index 2}
|
||||
:customization-color (:color account)
|
||||
:disabled? loading-fees?
|
||||
:disabled? (or loading-swap-proposal? (not swap-proposal))
|
||||
:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/confirm)}]))
|
||||
|
||||
|
|
|
@ -49,21 +49,18 @@
|
|||
(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?)]
|
||||
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])]
|
||||
[rn/view {:style style/details-container}
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-fees)
|
||||
:subtitle max-fees
|
||||
:loading? loading?
|
||||
:loading? loading-swap-proposal?
|
||||
:size :small}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-slippage)
|
||||
:subtitle max-slippage
|
||||
:subtitle-icon :i/edit
|
||||
:size :small
|
||||
:loading? loading?}]]))
|
||||
:size :small}]]))
|
||||
|
||||
(defn- pay-token-input
|
||||
[{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}]
|
||||
|
@ -88,7 +85,7 @@
|
|||
pay-token-fiat-value (str
|
||||
(utils/calculate-token-fiat-value
|
||||
{:currency currency
|
||||
:balance pay-input-num-value
|
||||
:balance (or pay-input-num-value 0)
|
||||
:token asset-to-pay}))
|
||||
available-crypto-limit (number/remove-trailing-zeroes
|
||||
(.toFixed (money/bignumber
|
||||
|
@ -209,7 +206,6 @@
|
|||
[{: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])
|
||||
approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status])]
|
||||
|
@ -219,8 +215,7 @@
|
|||
:button-one-props {:disabled? (or (not swap-proposal)
|
||||
(and approval-required?
|
||||
(not= approval-transaction-status :confirmed))
|
||||
loading-swap-proposal?
|
||||
loading-fees?)
|
||||
loading-swap-proposal?)
|
||||
:customization-color account-color
|
||||
:on-press on-press}}]))
|
||||
|
||||
|
@ -228,12 +223,29 @@
|
|||
[]
|
||||
(let [[pay-input-state set-pay-input-state] (rn/use-state controlled-input/init-state)
|
||||
[pay-input-focused? set-pay-input-focused?] (rn/use-state true)
|
||||
[refetch-interval set-refetch-interval] (rn/use-state nil)
|
||||
error-response (rf/sub [:wallet/swap-error-response])
|
||||
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
|
||||
network (rf/sub [:wallet/swap-network])
|
||||
pay-input-amount (controlled-input/input-value pay-input-state)
|
||||
pay-token-decimals (:decimals asset-to-pay)
|
||||
pay-input-num-value (controlled-input/numeric-value pay-input-state)
|
||||
pay-token-balance-selected-chain (get-in asset-to-pay
|
||||
[:balances-per-chain
|
||||
(:chain-id network) :balance]
|
||||
0)
|
||||
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?))
|
||||
on-review-swap-press (rn/use-callback
|
||||
(fn []
|
||||
(rf/dispatch [:navigate-to-within-stack
|
||||
|
@ -265,7 +277,35 @@
|
|||
(fn [input-state]
|
||||
(controlled-input/set-input-value
|
||||
input-state
|
||||
max-value)))))]
|
||||
max-value)))))
|
||||
on-refresh-swap-proposal (rn/use-callback
|
||||
(fn []
|
||||
(let [bottom-sheets (rf/sub
|
||||
[:bottom-sheet-sheets])]
|
||||
(when-not valid-pay-input?
|
||||
(js/clearInterval refetch-interval)
|
||||
(set-refetch-interval nil))
|
||||
(when (and valid-pay-input?
|
||||
(not loading-swap-proposal?)
|
||||
(not bottom-sheets))
|
||||
(fetch-swap-proposal
|
||||
{:amount pay-input-amount
|
||||
:valid-input? valid-pay-input?}))))
|
||||
[valid-pay-input? loading-swap-proposal?
|
||||
pay-input-amount])]
|
||||
(rn/use-effect (fn []
|
||||
(when refetch-interval
|
||||
(js/clearInterval refetch-interval)
|
||||
(set-refetch-interval nil))
|
||||
(when (or swap-proposal error-response)
|
||||
(set-refetch-interval (js/setInterval
|
||||
on-refresh-swap-proposal
|
||||
constants/swap-proposal-refresh-interval-ms))))
|
||||
[swap-proposal error-response])
|
||||
(rn/use-unmount (fn []
|
||||
(when refetch-interval
|
||||
(js/clearInterval refetch-interval)
|
||||
(set-refetch-interval nil))))
|
||||
[rn/view {:style style/container}
|
||||
[account-switcher/view
|
||||
{:on-press on-close
|
||||
|
|
|
@ -133,38 +133,42 @@
|
|||
|
||||
(defn- transaction-details
|
||||
[]
|
||||
(let [max-fees (rf/sub [:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||
constants/token-for-fees-symbol])
|
||||
estimated-time (rf/sub [:wallet/swap-proposal-estimated-time])
|
||||
loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
max-slippage (rf/sub [:wallet/swap-max-slippage])]
|
||||
(let [max-fees (rf/sub [:wallet/wallet-swap-proposal-fee-fiat-formatted
|
||||
constants/token-for-fees-symbol])
|
||||
estimated-time (rf/sub [:wallet/swap-proposal-estimated-time])
|
||||
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||
max-slippage (rf/sub [:wallet/swap-max-slippage])]
|
||||
[rn/view {:style style/details-container}
|
||||
[:<>
|
||||
[data-item
|
||||
{:title (i18n/label :t/est-time)
|
||||
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time)})}]
|
||||
:subtitle (if estimated-time
|
||||
(i18n/label :t/time-in-mins {:minutes (str estimated-time)})
|
||||
(i18n/label :t/unknown))
|
||||
:loading? loading-swap-proposal?}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-fees)
|
||||
:subtitle max-fees
|
||||
:loading? loading-fees?}]
|
||||
:subtitle (if (and estimated-time max-fees) max-fees (i18n/label :t/unknown))
|
||||
:loading? loading-swap-proposal?}]
|
||||
[data-item
|
||||
{:title (i18n/label :t/max-slippage)
|
||||
:subtitle (str max-slippage "%")}]]]))
|
||||
|
||||
(defn- slide-button
|
||||
[]
|
||||
(let [loading-fees? (rf/sub [:wallet/swap-loading-fees?])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
account-color (:color account)
|
||||
on-auth-success (rn/use-callback #(rf/dispatch
|
||||
[:wallet/swap-transaction
|
||||
(security/safe-unmask-data %)]))]
|
||||
(let [loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
|
||||
swap-proposal (rf/sub [:wallet/swap-proposal])
|
||||
account (rf/sub [:wallet/current-viewing-account])
|
||||
account-color (:color account)
|
||||
on-auth-success (rn/use-callback #(rf/dispatch
|
||||
[:wallet/swap-transaction
|
||||
(security/safe-unmask-data %)]))]
|
||||
[standard-auth/slide-button
|
||||
{:size :size-48
|
||||
:track-text (i18n/label :t/slide-to-swap)
|
||||
:container-style {:z-index 2}
|
||||
:customization-color account-color
|
||||
:disabled? loading-fees?
|
||||
:disabled? (or loading-swap-proposal? (not swap-proposal))
|
||||
:auth-button-label (i18n/label :t/confirm)
|
||||
:on-auth-success on-auth-success}]))
|
||||
|
||||
|
@ -179,7 +183,8 @@
|
|||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style (style/swaps-powered-by theme)}
|
||||
(i18n/label :t/swaps-powered-by {:provider (:full-name provider)})]]]))
|
||||
(i18n/label :t/swaps-powered-by
|
||||
{:provider (if provider (:full-name provider) (i18n/label :t/unknown))})]]]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
(ns status-im.subs.bottom-sheet
|
||||
(:require
|
||||
[re-frame.core :as re-frame]))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:bottom-sheet-sheets
|
||||
:<- [:bottom-sheet]
|
||||
:-> :sheets)
|
|
@ -4,6 +4,7 @@
|
|||
status-im.subs.activity-center
|
||||
status-im.subs.alert-banner
|
||||
status-im.subs.biometrics
|
||||
status-im.subs.bottom-sheet
|
||||
status-im.subs.chats
|
||||
status-im.subs.communities
|
||||
status-im.subs.community.account-selection
|
||||
|
|
|
@ -78,11 +78,6 @@
|
|||
:<- [:wallet/swap]
|
||||
:-> :max-slippage)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-loading-fees?
|
||||
:<- [:wallet/swap]
|
||||
:-> :loading-fees?)
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-approval-transaction-id
|
||||
:<- [:wallet/swap]
|
||||
|
@ -157,9 +152,10 @@
|
|||
:wallet/swap-proposal-provider
|
||||
:<- [:wallet/swap-proposal]
|
||||
(fn [swap-proposal]
|
||||
(let [bridge-name (:bridge-name swap-proposal)
|
||||
provider-key (keyword (string/lower-case bridge-name))]
|
||||
(get constants/swap-providers provider-key))))
|
||||
(when swap-proposal
|
||||
(let [bridge-name (:bridge-name swap-proposal)
|
||||
provider-key (keyword (string/lower-case bridge-name))]
|
||||
(get constants/swap-providers provider-key)))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/swap-proposal-approval-required
|
||||
|
|
|
@ -141,7 +141,6 @@
|
|||
:eip-1559-enabled true
|
||||
:l-1-gas-fee "0"}}
|
||||
:error-response "Error"
|
||||
:loading-fees? false
|
||||
:loading-swap-proposal? false
|
||||
:max-slippage 0.5})
|
||||
|
||||
|
@ -218,14 +217,6 @@
|
|||
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"
|
||||
|
|
Loading…
Reference in New Issue