feat(swap): trigger swap transactions (#21134)

Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
Brian Sztamfater 2024-09-06 11:30:15 -03:00 committed by GitHub
parent 3ae500ff68
commit e2d597401c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 414 additions and 249 deletions

View File

@ -9,6 +9,7 @@
[:token-value :string] [:token-value :string]
[:token-symbol :string] [:token-symbol :string]
[:container-style {:optional true} [:maybe :map]] [:container-style {:optional true} [:maybe :map]]
[:show-view-button? {:optional true} [:maybe :boolean]]
[:button-props {:optional true} [:maybe :map]] [:button-props {:optional true} [:maybe :map]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]] [:customization-color {:optional true} [:maybe :schema.common/customization-color]]]]]
:any]) :any])

View File

@ -24,7 +24,7 @@
(defn- view-internal (defn- view-internal
[{:keys [status token-value token-symbol [{:keys [status token-value token-symbol
container-style button-props] container-style button-props show-view-button?]
:as props}] :as props}]
(let [theme (quo.theme/use-theme) (let [theme (quo.theme/use-theme)
customization-color (or (:customization-color props) :blue) customization-color (or (:customization-color props) :blue)
@ -56,7 +56,9 @@
(i18n/label (status-message status) (i18n/label (status-message status)
{:amount token-value {:amount token-value
:symbol token-symbol})]] :symbol token-symbol})]]
(when button-props (when (and button-props
(or (= status :approve)
(and (not= status :approve) show-view-button?)))
[button/button [button/button
(merge {:type (if (= status :approve) :primary :grey) (merge {:type (if (= status :approve) :primary :grey)
:background (when-not (= status :approve) :blur) :background (when-not (= status :approve) :blur)

View File

@ -565,6 +565,7 @@
(def ^:const bridge-name-erc-721-transfer "ERC721Transfer") (def ^:const bridge-name-erc-721-transfer "ERC721Transfer")
(def ^:const bridge-name-erc-1155-transfer "ERC1155Transfer") (def ^:const bridge-name-erc-1155-transfer "ERC1155Transfer")
(def ^:const bridge-name-hop "Hop") (def ^:const bridge-name-hop "Hop")
(def ^:const bridge-name-paraswap "Paraswap")
(def ^:const bridge-assets #{"ETH" "USDT" "USDC" "DAI"}) (def ^:const bridge-assets #{"ETH" "USDT" "USDC" "DAI"})
@ -605,3 +606,5 @@
(def ^:const transaction-status-success "Success") (def ^:const transaction-status-success "Success")
(def ^:const transaction-status-pending "Pending") (def ^:const transaction-status-pending "Pending")
(def ^:const transaction-status-failed "Failed") (def ^:const transaction-status-failed "Failed")
(def ^:const min-token-decimals-to-display 6)

View File

@ -407,7 +407,8 @@
:TransferTx tx-data})) :TransferTx tx-data}))
(defn transaction-path (defn transaction-path
[{:keys [from-address to-address token-id token-address route data eth-transfer?]}] [{:keys [from-address to-address token-id-from token-address token-id-to route data
slippage-percentage eth-transfer?]}]
(let [{:keys [bridge-name amount-in bonder-fees from (let [{:keys [bridge-name amount-in bonder-fees from
to]} route to]} route
tx-data (transaction-data {:from-address from-address tx-data (transaction-data {:from-address from-address
@ -425,14 +426,14 @@
(assoc :ERC721TransferTx (assoc :ERC721TransferTx
(assoc tx-data (assoc tx-data
:Recipient to-address :Recipient to-address
:TokenID token-id :TokenID token-id-from
:ChainID to-chain-id)) :ChainID to-chain-id))
(= bridge-name constants/bridge-name-erc-1155-transfer) (= bridge-name constants/bridge-name-erc-1155-transfer)
(assoc :ERC1155TransferTx (assoc :ERC1155TransferTx
(assoc tx-data (assoc tx-data
:Recipient to-address :Recipient to-address
:TokenID token-id :TokenID token-id-from
:ChainID to-chain-id :ChainID to-chain-id
:Amount amount-in)) :Amount amount-in))
@ -444,18 +445,27 @@
(assoc tx-data (assoc tx-data
:ChainID from-chain-id :ChainID from-chain-id
:ChainIDTo to-chain-id :ChainIDTo to-chain-id
:Symbol token-id :Symbol token-id-from
:Recipient to-address :Recipient to-address
:Amount amount-in :Amount amount-in
:BonderFee bonder-fees)) :BonderFee bonder-fees))
(= bridge-name constants/bridge-name-paraswap)
(assoc :SwapTx
(assoc tx-data
:ChainID from-chain-id
:ChainIDTo to-chain-id
:TokenIDFrom token-id-from
:TokenIDTo token-id-to
:SlippagePercentage slippage-percentage))
(not (or (= bridge-name constants/bridge-name-erc-721-transfer) (not (or (= bridge-name constants/bridge-name-erc-721-transfer)
(= bridge-name constants/bridge-name-transfer) (= bridge-name constants/bridge-name-transfer)
(= bridge-name constants/bridge-name-hop))) (= bridge-name constants/bridge-name-hop)))
(assoc :CbridgeTx (assoc :CbridgeTx
(assoc tx-data (assoc tx-data
:ChainID to-chain-id :ChainID to-chain-id
:Symbol token-id :Symbol token-id-from
:Recipient to-address :Recipient to-address
:Amount amount-in))))) :Amount amount-in)))))

View File

@ -618,7 +618,7 @@
:from-address from-address :from-address from-address
:route route :route route
:token-address token-address :token-address token-address
:token-id (if collectible :token-id-from (if collectible
(utils.hex/number-to-hex (utils.hex/number-to-hex
token-id) token-id)
token-id) token-id)

View File

@ -20,10 +20,22 @@
(= tx-status constants/transaction-status-failed) (= tx-status constants/transaction-status-failed)
:failed) :failed)
swap-approval-transaction-id (get-in db [:wallet :ui :swap :approval-transaction-id]) swap-approval-transaction-id (get-in db [:wallet :ui :swap :approval-transaction-id])
swap-approval-transaction? (= swap-approval-transaction-id tx-hash)] swap-approval-transaction? (= swap-approval-transaction-id tx-hash)
swap-transaction-ids (get-in db [:wallet :swap-transaction-ids])
swap-transaction? (and swap-transaction-ids
(contains? swap-transaction-ids tx-hash))]
(cond-> {:db (update-in db [:wallet :transactions tx-hash] assoc :status status)} (cond-> {:db (update-in db [:wallet :transactions tx-hash] assoc :status status)}
swap-approval-transaction? swap-approval-transaction?
(assoc :fx [[:dispatch [:wallet.swap/approve-transaction-update status]]]))))) (assoc :fx
[[:dispatch
[:wallet.swap/approve-transaction-update
{:status status}]]])
swap-transaction?
(assoc :fx
[[:dispatch
[:wallet.swap/swap-transaction-update
{:tx-hash tx-hash
:status status}]]])))))
(rf/reg-event-fx (rf/reg-event-fx
:wallet/signal-received :wallet/signal-received

View File

@ -7,8 +7,11 @@
[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] [taoensso.timbre :as log]
[utils.address :as address] [utils.address :as address]
[utils.debounce :as debounce]
[utils.hex :as hex]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.number])) [utils.number]
[utils.number :as number]))
(rf/reg-event-fx :wallet.swap/start (rf/reg-event-fx :wallet.swap/start
(fn [{:keys [_db]}] (fn [{:keys [_db]}]
@ -95,6 +98,7 @@
:fromLockedAmount from-locked-amount} :fromLockedAmount from-locked-amount}
amount-in (assoc :amountIn amount-in-hex) amount-in (assoc :amountIn amount-in-hex)
amount-out (assoc :amountOut amount-out-hex))]] amount-out (assoc :amountOut amount-out-hex))]]
(when-let [amount (or amount-in amount-out)] (when-let [amount (or amount-in amount-out)]
{:db (update-in db {:db (update-in db
[:wallet :ui :swap] [:wallet :ui :swap]
@ -165,30 +169,38 @@
(rf/reg-event-fx :wallet/swap-transaction (rf/reg-event-fx :wallet/swap-transaction
(fn [{:keys [db]} [sha3-pwd]] (fn [{:keys [db]} [sha3-pwd]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address]) (let [wallet-address (get-in db
{:keys [asset-to-pay swap-proposal network [:wallet
approval-transaction-id]} (get-in db [:wallet :ui :swap]) :current-viewing-account-address])
{:keys [asset-to-pay asset-to-receive
swap-proposal network amount
approval-transaction-id
max-slippage]} (get-in db [:wallet :ui :swap])
transactions (get-in db [:wallet :transactions]) transactions (get-in db [:wallet :transactions])
approval-transaction (when approval-transaction-id approval-transaction (when approval-transaction-id
(get transactions approval-transaction-id)) (get transactions approval-transaction-id))
already-approved? (and approval-transaction already-approved? (and approval-transaction
(= (:status approval-transaction) :confirmed)) (= (:status approval-transaction)
:confirmed))
approval-required? (and (:approval-required swap-proposal) approval-required? (and (:approval-required swap-proposal)
(not already-approved?)) (not already-approved?))
multi-transaction-type constants/multi-transaction-type-swap multi-transaction-type constants/multi-transaction-type-swap
swap-chain-id (:chain-id network) swap-chain-id (:chain-id network)
token-id (:symbol asset-to-pay) token-id-from (:symbol asset-to-pay)
erc20-transfer? (and asset-to-pay (not= token-id "ETH")) token-id-to (:symbol asset-to-receive)
erc20-transfer? (and asset-to-pay (not= token-id-from "ETH"))
eth-transfer? (and asset-to-pay (not erc20-transfer?)) eth-transfer? (and asset-to-pay (not erc20-transfer?))
token-address (when erc20-transfer? token-address (when erc20-transfer?
(get-in asset-to-pay (get-in asset-to-pay
[:balances-per-chain swap-chain-id :address])) [:balances-per-chain swap-chain-id
:address]))
data (when erc20-transfer? data (when erc20-transfer?
(native-module/encode-transfer (native-module/encode-transfer
(address/normalized-hex wallet-address) (address/normalized-hex wallet-address)
(:amount-in swap-proposal))) (:amount-in swap-proposal)))
transaction-paths (if approval-required? transaction-paths (if approval-required?
[(utils/approval-path {:route swap-proposal [(utils/approval-path
{:route swap-proposal
:token-address token-address :token-address token-address
:from-address wallet-address :from-address wallet-address
:to-address wallet-address})] :to-address wallet-address})]
@ -197,18 +209,23 @@
:from-address wallet-address :from-address wallet-address
:route swap-proposal :route swap-proposal
:token-address token-address :token-address token-address
:token-id token-id :token-id-from token-id-from
:token-id-to token-id-to
:data data :data data
:slippage-percentage max-slippage
:eth-transfer? eth-transfer?})]) :eth-transfer? eth-transfer?})])
request-params [(utils/multi-transaction-command request-params [(utils/multi-transaction-command
{:from-address wallet-address {:from-address wallet-address
:to-address wallet-address :to-address wallet-address
:from-asset token-id :from-asset token-id-from
:to-asset token-id :to-asset (if approval-required?
token-id-from
token-id-to)
:amount-out (if eth-transfer? :amount-out (if eth-transfer?
(:amount-out swap-proposal) (:amount-out swap-proposal)
"0x0") "0x0")
:multi-transaction-type multi-transaction-type}) :multi-transaction-type
multi-transaction-type})
transaction-paths transaction-paths
sha3-pwd]] sha3-pwd]]
(log/info "multi transaction called") (log/info "multi transaction called")
@ -216,12 +233,49 @@
:params request-params :params request-params
:on-success (fn [result] :on-success (fn [result]
(when result (when result
(let [receive-token-decimals (:decimals asset-to-receive)
amount-out (:amount-out swap-proposal)
decimals-to-display
(min
receive-token-decimals
constants/min-token-decimals-to-display)
receive-amount (when amount-out
(number/remove-trailing-zeroes
(.toFixed (number/convert-to-whole-number
(native-module/hex-to-number
(hex/normalize-hex
amount-out))
receive-token-decimals)
decimals-to-display)))]
(rf/dispatch [:wallet.swap/add-authorized-transaction (rf/dispatch [:wallet.swap/add-authorized-transaction
{:transaction result (cond-> {:transaction result
:approval-transaction? approval-required?}]) :approval-transaction?
approval-required?}
(not approval-required?)
(assoc :swap-data
{:pay-token-symbol token-id-from
:pay-amount amount
:receive-token-symbol token-id-to
:receive-amount receive-amount}))])
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:dismiss-modal (rf/dispatch [:dismiss-modal
:screen/wallet.swap-set-spending-cap]) (if approval-required?
(rf/dispatch [:hide-bottom-sheet]))) :screen/wallet.swap-set-spending-cap
:screen/wallet.swap-confirmation)])
(when-not approval-required?
(rf/dispatch [:wallet/select-account-tab :activity])
(debounce/debounce-and-dispatch [:wallet/clean-swap] 1000)
(debounce/debounce-and-dispatch
[:toasts/upsert
{:id :swap-transaction-pending
:icon :i/info
:type :neutral
:text (i18n/label :t/swapping-to
{:pay-amount amount
:pay-token-symbol token-id-from
:receive-token-symbol token-id-to
:receive-amount receive-amount})}]
500)))))
:on-error (fn [error] :on-error (fn [error]
(log/error "failed swap transaction" (log/error "failed swap transaction"
{:event :wallet/swap-transaction {:event :wallet/swap-transaction
@ -233,20 +287,30 @@
:text (:message error)}]))}]}))) :text (:message error)}]))}]})))
(rf/reg-event-fx :wallet.swap/add-authorized-transaction (rf/reg-event-fx :wallet.swap/add-authorized-transaction
(fn [{:keys [db]} [{:keys [transaction approval-transaction?]}]] (fn [{:keys [db]} [{:keys [transaction swap-data approval-transaction?]}]]
(let [transaction-batch-id (:id transaction) (let [transactions (get-in db [:wallet :transactions] {})
transaction-batch-id (:id transaction)
transaction-hashes (:hashes transaction) transaction-hashes (:hashes transaction)
transaction-ids (flatten (vals transaction-hashes)) transaction-ids (flatten (vals transaction-hashes))
transaction-details (send-utils/map-multitransaction-by-ids transaction-batch-id transaction-id (first transaction-ids)
transaction-hashes)] transaction-details (cond-> (send-utils/map-multitransaction-by-ids transaction-batch-id
transaction-hashes)
:always (assoc-in [transaction-id :tx-type] :swap)
swap-data (assoc-in [transaction-id :swap-data] swap-data))
swap-transaction-ids (get-in db [:wallet :swap-transaction-ids])]
{:db (cond-> db {:db (cond-> db
:always (assoc-in [:wallet :transactions] transaction-details) :always (assoc-in [:wallet :transactions]
(merge transactions transaction-details))
:always (assoc-in [:wallet :ui :swap :transaction-ids] transaction-ids) :always (assoc-in [:wallet :ui :swap :transaction-ids] transaction-ids)
approval-transaction? (assoc-in [:wallet :ui :swap :approval-transaction-id] approval-transaction? (assoc-in [:wallet :ui :swap :approval-transaction-id]
(first transaction-ids)))}))) transaction-id)
(not approval-transaction?) (assoc-in [:wallet :swap-transaction-ids]
(if swap-transaction-ids
(conj swap-transaction-ids transaction-id)
(hash-set transaction-id))))})))
(rf/reg-event-fx :wallet.swap/approve-transaction-update (rf/reg-event-fx :wallet.swap/approve-transaction-update
(fn [{:keys [db]} [status]] (fn [{:keys [db]} [{:keys [status]}]]
(let [{:keys [amount asset-to-pay swap-proposal]} (get-in db [:wallet :ui :swap]) (let [{:keys [amount asset-to-pay swap-proposal]} (get-in db [:wallet :ui :swap])
provider-name (:bridge-name swap-proposal) provider-name (:bridge-name swap-proposal)
token-symbol (:symbol asset-to-pay) token-symbol (:symbol asset-to-pay)
@ -276,3 +340,27 @@
:account-name account-name}))}]]]} :account-name account-name}))}]]]}
(not transaction-confirmed?) (not transaction-confirmed?)
(assoc :db (update-in db [:wallet :ui :swap] dissoc :approval-transaction-id))))))) (assoc :db (update-in db [:wallet :ui :swap] dissoc :approval-transaction-id)))))))
(rf/reg-event-fx :wallet.swap/swap-transaction-update
(fn [{:keys [db]} [{:keys [tx-hash status]}]]
(let [{:keys [pay-amount pay-token-symbol
receive-amount receive-token-symbol]} (get-in db
[:wallet :transactions tx-hash
:swap-data])
transaction-confirmed-or-failed? (#{:confirmed :failed} status)
transaction-confirmed? (= status :confirmed)]
(when transaction-confirmed-or-failed?
{:db (-> db
(update-in [:wallet :swap-transaction-ids] disj tx-hash)
(update-in [:wallet :transactions] dissoc tx-hash))
:fx [[:dispatch
[:toasts/upsert
{:id :swap-transaction-update
:type (if transaction-confirmed? :positive :negative)
:text (if transaction-confirmed?
(i18n/label :t/swapped-to
{:pay-amount pay-amount
:pay-token-symbol pay-token-symbol
:receive-token-symbol receive-token-symbol
:receive-amount receive-amount})
(i18n/label :t/swap-failed))}]]]}))))

View File

@ -22,7 +22,7 @@
[search-text on-change-text] [search-text on-change-text]
(let [on-token-press (fn [token] (let [on-token-press (fn [token]
(let [token-networks (:networks token) (let [token-networks (:networks token)
asset-to-receive (rf/sub [:wallet/token-by-symbol "SNT"])] asset-to-receive (rf/sub [:wallet/token-by-symbol "DAI"])]
(rf/dispatch [:wallet.swap/select-asset-to-pay (rf/dispatch [:wallet.swap/select-asset-to-pay
{:token token {:token token
:network (when (= (count token-networks) 1) :network (when (= (count token-networks) 1)

View File

@ -1,6 +1,5 @@
(ns status-im.contexts.wallet.swap.set-spending-cap.view (ns status-im.contexts.wallet.swap.set-spending-cap.view
(:require (:require
[native-module.core :as native-module]
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.resources :as resources] [quo.foundations.resources :as resources]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
@ -12,26 +11,17 @@
[status-im.contexts.wallet.common.utils.external-links :as external-links] [status-im.contexts.wallet.common.utils.external-links :as external-links]
[status-im.contexts.wallet.swap.set-spending-cap.style :as style] [status-im.contexts.wallet.swap.set-spending-cap.style :as style]
[utils.address :as address-utils] [utils.address :as address-utils]
[utils.hex :as hex]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.number :as number]
[utils.re-frame :as rf] [utils.re-frame :as rf]
[utils.security.core :as security])) [utils.security.core :as security]))
(defn- swap-title (defn- swap-title
[] []
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay]) (let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
amount-in (rf/sub [:wallet/swap-proposal-amount-in]) pay-amount (rf/sub [:wallet/swap-pay-amount])
account (rf/sub [:wallet/current-viewing-account]) account (rf/sub [:wallet/current-viewing-account])
provider (rf/sub [:wallet/swap-proposal-provider]) provider (rf/sub [:wallet/swap-proposal-provider])
pay-token-symbol (:symbol asset-to-pay) pay-token-symbol (:symbol asset-to-pay)]
pay-token-decimals (:decimals asset-to-pay)
pay-amount (when amount-in
(number/convert-to-whole-number
(native-module/hex-to-number
(hex/normalize-hex
amount-in))
pay-token-decimals))]
[rn/view {:style style/content-container} [rn/view {:style style/content-container}
[rn/view {:style {:flex-direction :row}} [rn/view {:style {:flex-direction :row}}
[quo/text [quo/text
@ -74,15 +64,8 @@
[] []
(let [theme (quo.theme/use-theme) (let [theme (quo.theme/use-theme)
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay]) asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
amount-in (rf/sub [:wallet/swap-proposal-amount-in]) pay-amount (rf/sub [:wallet/swap-pay-amount])
pay-token-symbol (:symbol asset-to-pay) pay-token-symbol (:symbol asset-to-pay)]
pay-token-decimals (:decimals asset-to-pay)
pay-amount (when amount-in
(number/convert-to-whole-number
(native-module/hex-to-number
(hex/normalize-hex
amount-in))
pay-token-decimals))]
[rn/view {:style style/summary-section-container} [rn/view {:style style/summary-section-container}
[quo/text [quo/text
{:size :paragraph-2 {:size :paragraph-2
@ -100,16 +83,9 @@
[] []
(let [theme (quo.theme/use-theme) (let [theme (quo.theme/use-theme)
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay]) asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
amount-in (rf/sub [:wallet/swap-proposal-amount-in])
account (rf/sub [:wallet/current-viewing-account]) account (rf/sub [:wallet/current-viewing-account])
pay-token-symbol (:symbol asset-to-pay) pay-amount (rf/sub [:wallet/swap-pay-amount])
pay-token-decimals (:decimals asset-to-pay) pay-token-symbol (:symbol asset-to-pay)]
pay-amount (when amount-in
(number/convert-to-whole-number
(native-module/hex-to-number
(hex/normalize-hex
amount-in))
pay-token-decimals))]
[rn/view {:style style/summary-section-container} [rn/view {:style style/summary-section-container}
[quo/text [quo/text
{:size :paragraph-2 {:size :paragraph-2
@ -216,14 +192,14 @@
{:title (i18n/label :t/network) {:title (i18n/label :t/network)
:subtitle (:full-name network) :subtitle (:full-name network)
:network-image (:source network)}] :network-image (:source network)}]
[data-item
{:title (i18n/label :t/est-time)
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time)})}]
[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-fees?
:size :small}]]])) :size :small}]
[data-item
{:title (i18n/label :t/est-time)
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time)})}]]]))
(defn- slide-button (defn- slide-button
[] []
@ -234,7 +210,7 @@
(security/safe-unmask-data %)]))] (security/safe-unmask-data %)]))]
[standard-auth/slide-button [standard-auth/slide-button
{:size :size-48 {:size :size-48
:track-text (i18n/label :t/slide-to-swap) :track-text (i18n/label :t/slide-to-sign)
:container-style {:z-index 2} :container-style {:z-index 2}
:customization-color (:color account) :customization-color (:color account)
:disabled? loading-fees? :disabled? loading-fees?

View File

@ -3,6 +3,7 @@
[native-module.core :as native-module] [native-module.core :as native-module]
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[react-native.platform :as platform]
[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.controlled-input.utils :as controlled-input]
[status-im.common.events-helper :as events-helper] [status-im.common.events-helper :as events-helper]
@ -17,7 +18,6 @@
[utils.re-frame :as rf] [utils.re-frame :as rf]
[utils.string :as utils.string])) [utils.string :as utils.string]))
(def ^:private min-token-decimals-to-display 6)
(def ^:private default-text-for-unfocused-input "0.00") (def ^:private default-text-for-unfocused-input "0.00")
(defn- on-close (defn- on-close
@ -94,7 +94,7 @@
(.toFixed (money/bignumber (.toFixed (money/bignumber
pay-token-balance-selected-chain) pay-token-balance-selected-chain)
(min pay-token-decimals (min pay-token-decimals
min-token-decimals-to-display))) constants/min-token-decimals-to-display)))
approval-amount-required-num (when approval-amount-required approval-amount-required-num (when approval-amount-required
(str (number/convert-to-whole-number (str (number/convert-to-whole-number
(native-module/hex-to-number (native-module/hex-to-number
@ -128,13 +128,14 @@
:customization-color :blue :customization-color :blue
:show-approval-label? (and swap-proposal approval-required) :show-approval-label? (and swap-proposal approval-required)
:auto-focus? true :auto-focus? true
:show-keyboard? false
:status (cond :status (cond
(and loading-swap-proposal? (not input-focused?)) :loading (and loading-swap-proposal? (not input-focused?)) :loading
input-focused? :typing input-focused? :typing
:else :disabled) :else :disabled)
:currency-symbol currency-symbol :currency-symbol currency-symbol
:on-token-press on-token-press :on-token-press on-token-press
:on-max-press on-max-press :on-max-press #(on-max-press available-crypto-limit)
:on-input-focus on-input-focus :on-input-focus on-input-focus
:value pay-input-amount :value pay-input-amount
:fiat-value pay-token-fiat-value :fiat-value pay-token-fiat-value
@ -177,7 +178,8 @@
amount-out)) amount-out))
receive-token-decimals)) receive-token-decimals))
amount-out-num (if amount-out-whole-number amount-out-num (if amount-out-whole-number
(str amount-out-whole-number) (number/remove-trailing-zeroes
(.toFixed amount-out-whole-number receive-token-decimals))
default-text-for-unfocused-input) default-text-for-unfocused-input)
receive-token-fiat-value (str (utils/calculate-token-fiat-value receive-token-fiat-value (str (utils/calculate-token-fiat-value
{:currency currency {:currency currency
@ -191,6 +193,7 @@
:show-approval-label? false :show-approval-label? false
:enable-swap? true :enable-swap? true
:input-disabled? true :input-disabled? true
:show-keyboard? false
:status (cond :status (cond
(and loading-swap-proposal? (not input-focused?)) :loading (and loading-swap-proposal? (not input-focused?)) :loading
input-focused? :typing input-focused? :typing
@ -226,17 +229,16 @@
(let [[pay-input-state set-pay-input-state] (rn/use-state controlled-input/init-state) (let [[pay-input-state set-pay-input-state] (rn/use-state controlled-input/init-state)
[pay-input-focused? set-pay-input-focused?] (rn/use-state true) [pay-input-focused? set-pay-input-focused?] (rn/use-state true)
error-response (rf/sub [:wallet/swap-error-response]) error-response (rf/sub [:wallet/swap-error-response])
network (rf/sub [:wallet/swap-network])
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?]) loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal]) 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])
pay-input-amount (controlled-input/input-value pay-input-state) pay-input-amount (controlled-input/input-value pay-input-state)
pay-token-decimals (:decimals asset-to-pay) pay-token-decimals (:decimals asset-to-pay)
network-chain-id (:chain-id network) on-review-swap-press (rn/use-callback
pay-token-balance-selected-chain (get-in asset-to-pay (fn []
[:balances-per-chain network-chain-id (rf/dispatch [:navigate-to-within-stack
:balance] [:screen/wallet.swap-confirmation
0) :screen/wallet.setup-swap]])))
on-press (rn/use-callback on-press (rn/use-callback
(fn [c] (fn [c]
(let (let
@ -256,7 +258,14 @@
(fn [] (fn []
(set-pay-input-state (set-pay-input-state
controlled-input/delete-last) controlled-input/delete-last)
(rf/dispatch [:wallet/clean-swap-proposal])))] (rf/dispatch [:wallet/clean-swap-proposal])))
on-max-press (rn/use-callback
(fn [max-value]
(set-pay-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
max-value)))))]
[rn/view {:style style/container} [rn/view {:style style/container}
[account-switcher/view [account-switcher/view
{:on-press on-close {:on-press on-close
@ -266,11 +275,13 @@
[rn/view {:style style/inputs-container} [rn/view {:style style/inputs-container}
[pay-token-input [pay-token-input
{:input-state pay-input-state {:input-state pay-input-state
:on-max-press #(set-pay-input-state pay-token-balance-selected-chain) :on-max-press on-max-press
:input-focused? pay-input-focused? :input-focused? pay-input-focused?
:on-token-press #(js/alert "Token Pressed") :on-token-press #(js/alert "Token Pressed")
:on-approve-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap]) :on-approve-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])
:on-input-focus #(set-pay-input-focused? true)}] :on-input-focus (fn []
(when platform/android? (rf/dispatch [:dismiss-keyboard]))
(set-pay-input-focused? true))}]
[swap-order-button {:on-press #(js/alert "Swap Order Pressed")}] [swap-order-button {:on-press #(js/alert "Swap Order Pressed")}]
[receive-token-input [receive-token-input
{:input-focused? (not pay-input-focused?) {:input-focused? (not pay-input-focused?)
@ -283,8 +294,7 @@
:text (i18n/label :t/something-went-wrong-please-try-again-later)}]) :text (i18n/label :t/something-went-wrong-please-try-again-later)}])
(when (or loading-swap-proposal? swap-proposal) (when (or loading-swap-proposal? swap-proposal)
[transaction-details]) [transaction-details])
[action-button [action-button {:on-press on-review-swap-press}]]
{: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

View File

@ -37,3 +37,7 @@
(def providers-container (def providers-container
{:align-items :center {:align-items :center
:margin-top 12}) :margin-top 12})
(defn swaps-powered-by
[theme]
{:color (colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-70 theme)})

View File

@ -1,23 +1,31 @@
(ns status-im.contexts.wallet.swap.swap-confirmation.view (ns status-im.contexts.wallet.swap.swap-confirmation.view
(:require (:require
[quo.core :as quo] [quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme] [quo.theme :as quo.theme]
[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.floating-button-page.view :as floating-button-page] [status-im.common.floating-button-page.view :as floating-button-page]
[status-im.common.standard-authentication.core :as standard-auth] [status-im.common.standard-authentication.core :as standard-auth]
[status-im.constants :as constants]
[status-im.contexts.wallet.swap.swap-confirmation.style :as style] [status-im.contexts.wallet.swap.swap-confirmation.style :as style]
[utils.address :as address-utils] [utils.address :as address-utils]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.re-frame :as rf])) [utils.re-frame :as rf]
[utils.security.core :as security]))
(defn- on-close-action (defn- on-close-action
[] []
(rf/dispatch [:navigate-back])) (rf/dispatch [:navigate-back]))
(defn- swap-title (defn- swap-title
[{:keys [pay-token-symbol pay-amount receive-token-symbol receive-amount account]}] []
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
account (rf/sub [:wallet/current-viewing-account])
receive-amount (rf/sub [:wallet/swap-receive-amount])
pay-amount (rf/sub [:wallet/swap-pay-amount])
pay-token-symbol (:symbol asset-to-pay)
receive-token-symbol (:symbol asset-to-receive)]
[rn/view {:style style/content-container} [rn/view {:style style/content-container}
[rn/view {:style {:flex-direction :row}} [rn/view {:style {:flex-direction :row}}
[quo/text [quo/text
@ -52,7 +60,7 @@
{:label (:name account) {:label (:name account)
:type :account :type :account
:emoji (:emoji account) :emoji (:emoji account)
:customization-color (:color account)}]]]) :customization-color (:color account)}]]]))
(defn- summary-section (defn- summary-section
[{:keys [theme label title-accessibility-label amount token-symbol token-address network]}] [{:keys [theme label title-accessibility-label amount token-symbol token-address network]}]
@ -74,6 +82,44 @@
:address (address-utils/get-shortened-compressed-key token-address) :address (address-utils/get-shortened-compressed-key token-address)
:size 32}}]])) :size 32}}]]))
(defn- pay-section
[]
(let [theme (quo.theme/use-theme)
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
network (rf/sub [:wallet/swap-network])
pay-amount (rf/sub [:wallet/swap-pay-amount])
network-chain-id (:chain-id network)
network-name (:network-name network)
pay-token-symbol (:symbol asset-to-pay)
pay-token-address (get-in asset-to-pay [:balances-per-chain network-chain-id :address])]
[summary-section
{:title-accessibility-label :summary-section-pay
:label (i18n/label :t/pay)
:token-symbol pay-token-symbol
:amount pay-amount
:token-address pay-token-address
:network network-name
:theme theme}]))
(defn- receive-section
[]
(let [theme (quo.theme/use-theme)
asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
network (rf/sub [:wallet/swap-network])
receive-amount (rf/sub [:wallet/swap-receive-amount])
network-chain-id (:chain-id network)
network-name (:network-name network)
receive-token-symbol (:symbol asset-to-receive)
receive-token-address (get-in asset-to-receive [:balances-per-chain network-chain-id :address])]
[summary-section
{:title-accessibility-label :summary-section-receive
:label (i18n/label :t/receive)
:token-symbol receive-token-symbol
:amount receive-amount
:token-address receive-token-address
:network network-name
:theme theme}]))
(defn- data-item (defn- data-item
[{:keys [title subtitle loading?]}] [{:keys [title subtitle loading?]}]
[quo/data-item [quo/data-item
@ -86,64 +132,59 @@
:subtitle subtitle}]) :subtitle subtitle}])
(defn- transaction-details (defn- transaction-details
[{:keys [estimated-time-min max-fees max-slippage loading-fees?]}] []
(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])]
[rn/view {:style style/details-container} [rn/view {:style style/details-container}
[:<> [:<>
[data-item [data-item
{:title (i18n/label :t/est-time) {:title (i18n/label :t/est-time)
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time-min)})}] :subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time)})}]
[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-fees?}]
[data-item [data-item
{:title (i18n/label :t/max-slippage) {:title (i18n/label :t/max-slippage)
:subtitle (str max-slippage "%")}]]]) :subtitle (str max-slippage "%")}]]]))
(defn footer (defn- slide-button
[{:keys [estimated-time-min native-currency-symbol max-slippage theme account-color provider []
loading-fees?]}] (let [loading-fees? (rf/sub [:wallet/swap-loading-fees?])
(let [fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])] account (rf/sub [:wallet/current-viewing-account])
[:<> account-color (:color account)
[transaction-details on-auth-success (rn/use-callback #(rf/dispatch
{:estimated-time-min estimated-time-min [:wallet/swap-transaction
:max-fees fee-formatted (security/safe-unmask-data %)]))]
:max-slippage max-slippage
:loading-fees? loading-fees?
:theme theme}]
[standard-auth/slide-button [standard-auth/slide-button
{:size :size-48 {:size :size-48
:track-text (i18n/label :t/slide-to-swap) :track-text (i18n/label :t/slide-to-swap)
:container-style {:z-index 2} :container-style {:z-index 2}
:customization-color account-color :customization-color account-color
:disabled? loading-fees? :disabled? loading-fees?
:auth-button-label (i18n/label :t/confirm)}] :auth-button-label (i18n/label :t/confirm)
:on-auth-success on-auth-success}]))
(defn footer
[]
(let [provider (rf/sub [:wallet/swap-proposal-provider])
theme (quo.theme/use-theme)]
[:<>
[transaction-details]
[slide-button]
[rn/view {:style style/providers-container} [rn/view {:style style/providers-container}
[quo/text [quo/text
{:size :paragraph-2 {:size :paragraph-2
:style {:color (colors/theme-colors colors/neutral-80-opa-40 :style (style/swaps-powered-by theme)}
colors/white-opa-70 (i18n/label :t/swaps-powered-by {:provider (:full-name provider)})]]]))
theme)}}
(i18n/label :t/swaps-powered-by {:provider (:name provider)})]]]))
(defn view (defn view
[] []
(let [theme (quo.theme/use-theme) (let [account (rf/sub [:wallet/current-viewing-account])
swap-transaction-data (rf/sub [:wallet/swap]) account-color (:color account)]
{:keys [asset-to-pay max-slippage network
pay-amount providers swap-proposal
loading-fees?]} swap-transaction-data
receive-amount (:receive-amount swap-proposal)
receive-token (:receive-token swap-proposal)
receive-token-symbol (:symbol receive-token)
receive-token-address (:address receive-token)
estimated-time-min (:estimated-time swap-proposal)
pay-token-symbol (:symbol asset-to-pay)
pay-token-address (:address asset-to-pay)
native-currency-symbol (get-in swap-proposal [:from :native-currency-symbol])
account (rf/sub [:wallet/current-viewing-account])
account-color (:color account)
provider (first providers)]
[rn/view {:style {:flex 1}} [rn/view {:style {:flex 1}}
[floating-button-page/view [floating-button-page/view
{:footer-container-padding 0 {:footer-container-padding 0
@ -153,36 +194,10 @@
:margin-top (safe-area/get-top) :margin-top (safe-area/get-top)
:background :blur :background :blur
:accessibility-label :top-bar}] :accessibility-label :top-bar}]
:footer [footer :footer [footer]
{:estimated-time-min estimated-time-min
:native-currency-symbol native-currency-symbol
:max-slippage max-slippage
:account-color account-color
:provider provider
:loading-fees? loading-fees?
:theme theme}]
:gradient-cover? true :gradient-cover? true
:customization-color account-color} :customization-color account-color}
[rn/view [rn/view
[swap-title [swap-title]
{:pay-token-symbol pay-token-symbol [pay-section]
:pay-amount pay-amount [receive-section]]]]))
:receive-token-symbol receive-token-symbol
:receive-amount receive-amount
:account account}]
[summary-section
{:title-accessibility-label :summary-section-pay
:label (i18n/label :t/pay)
:token-symbol pay-token-symbol
:amount pay-amount
:token-address pay-token-address
:network (:network-name network)
:theme theme}]
[summary-section
{:title-accessibility-label :summary-section-receive
:label (i18n/label :t/receive)
:token-symbol receive-token-symbol
:amount receive-amount
:token-address receive-token-address
:network (:network-name network)
:theme theme}]]]]))

View File

@ -1,10 +1,13 @@
(ns status-im.subs.wallet.swap (ns status-im.subs.wallet.swap
(:require [clojure.string :as string] (:require [clojure.string :as string]
[native-module.core :as native-module]
[re-frame.core :as rf] [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] [status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money])) [utils.hex :as hex]
[utils.money :as money]
[utils.number :as number]))
(rf/reg-sub (rf/reg-sub
:wallet/swap :wallet/swap
@ -112,6 +115,44 @@
:<- [:wallet/swap-proposal] :<- [:wallet/swap-proposal]
:-> :amount-in) :-> :amount-in)
(rf/reg-sub
:wallet/swap-receive-amount
:<- [:wallet/swap-proposal-amount-out]
:<- [:wallet/swap-asset-to-receive]
(fn [[amount-out asset-to-receive]]
(let [receive-token-decimals (:decimals asset-to-receive)
receive-amount (when amount-out
(number/convert-to-whole-number
(native-module/hex-to-number
(hex/normalize-hex
amount-out))
receive-token-decimals))
receive-amount (when amount-out
(number/remove-trailing-zeroes
(.toFixed receive-amount
(min receive-token-decimals
constants/min-token-decimals-to-display))))]
(or receive-amount 0))))
(rf/reg-sub
:wallet/swap-pay-amount
:<- [:wallet/swap-proposal-amount-in]
:<- [:wallet/swap-asset-to-pay]
(fn [[amount-in asset-to-pay]]
(let [pay-token-decimals (:decimals asset-to-pay)
pay-amount (when amount-in
(number/convert-to-whole-number
(native-module/hex-to-number
(hex/normalize-hex
amount-in))
pay-token-decimals))
pay-amount (when amount-in
(number/remove-trailing-zeroes
(.toFixed pay-amount
(min pay-token-decimals
constants/min-token-decimals-to-display))))]
(or pay-amount 0))))
(rf/reg-sub (rf/reg-sub
:wallet/swap-proposal-provider :wallet/swap-proposal-provider
:<- [:wallet/swap-proposal] :<- [:wallet/swap-proposal]

View File

@ -2368,6 +2368,9 @@
"sun": "Sun", "sun": "Sun",
"swap": "Swap", "swap": "Swap",
"swap-details": "Swap details", "swap-details": "Swap details",
"swap-failed": "Swap failed",
"swapped-to": "Swapped {{pay-amount}} {{pay-token-symbol}} to {{receive-amount}} {{receive-token-symbol}}",
"swapping-to": "Swapping {{pay-amount}} {{pay-token-symbol}} to {{receive-amount}} {{receive-token-symbol}}",
"swaps-powered-by": "Swaps powered by {{provider}}", "swaps-powered-by": "Swaps powered by {{provider}}",
"switch-to-simple-interface": "Switch to simple interface", "switch-to-simple-interface": "Switch to simple interface",
"symbol": "Symbol", "symbol": "Symbol",