feat: autorefresh swap proposal (#21143)

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2024-09-09 01:27:31 -03:00 committed by GitHub
parent cf5a395b29
commit 337ec963b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 155 additions and 99 deletions

View File

@ -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)

View File

@ -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]]

View File

@ -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)}]))

View File

@ -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

View File

@ -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
[]

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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"