diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 688328af0b..d769362282 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -591,11 +591,17 @@ (def ^:const default-slippage 0.5) (def ^:const max-recommended-slippage 5) (def ^:const max-slippage-decimal-places 2) -(def ^:const swap-default-provider +(def ^:const swap-provider-paraswap {:name :paraswap :full-name "Paraswap" :color :blue :contract-address "0xdef171fe48cf0115b1d80b88dc8eab59176fee57" :terms-and-conditions-url "https://files.paraswap.io/tos_v4.pdf"}) +(def ^:const swap-providers + {:paraswap swap-provider-paraswap}) (def ^:const token-for-fees-symbol "ETH") + +(def ^:const transaction-status-success "Success") +(def ^:const transaction-status-pending "Pending") +(def ^:const transaction-status-failed "Failed") diff --git a/src/status_im/contexts/wallet/common/utils.cljs b/src/status_im/contexts/wallet/common/utils.cljs index dde72dd84e..d13fd97527 100644 --- a/src/status_im/contexts/wallet/common/utils.cljs +++ b/src/status_im/contexts/wallet/common/utils.cljs @@ -1,8 +1,10 @@ (ns status-im.contexts.wallet.common.utils (:require [clojure.string :as string] + [native-module.core :as native-module] [quo.foundations.resources :as resources] [status-im.common.qr-codes.view :as qr-codes] [status-im.constants :as constants] + [utils.hex :as utils.hex] [utils.money :as money] [utils.number :as number] [utils.string])) @@ -348,3 +350,121 @@ [tokens] (let [priority #(get constants/token-sort-priority (:symbol %) ##Inf)] (sort-by (juxt (comp - :balance) priority) tokens))) + +(defn- transaction-data + [{:keys [from-address to-address token-address route data eth-transfer?]}] + (let [{:keys [amount-in gas-amount gas-fees]} route + eip-1559-enabled? (:eip-1559-enabled gas-fees) + {:keys [gas-price max-fee-per-gas-medium + max-priority-fee-per-gas]} gas-fees] + (cond-> {:From from-address + :To (or token-address to-address) + :Gas (money/to-hex gas-amount) + :Value (when eth-transfer? amount-in) + :Nonce nil + :Input "" + :Data (or data "0x")} + eip-1559-enabled? (assoc + :TxType "0x02" + :MaxFeePerGas + (money/to-hex + (money/->wei + :gwei + max-fee-per-gas-medium)) + :MaxPriorityFeePerGas + (money/to-hex + (money/->wei + :gwei + max-priority-fee-per-gas))) + (not eip-1559-enabled?) (assoc :TxType "0x00" + :GasPrice + (money/to-hex + (money/->wei + :gwei + gas-price)))))) + +(defn approval-path + [{:keys [route from-address to-address token-address]}] + (let [{:keys [from]} route + from-chain-id (:chain-id from) + approval-amount-required (:approval-amount-required route) + approval-amount-required-sanitized (-> approval-amount-required + (utils.hex/normalize-hex) + (native-module/hex-to-number)) + approval-contract-address (:approval-contract-address route) + data (native-module/encode-function-call + constants/contract-function-signature-erc20-approve + [approval-contract-address + approval-amount-required-sanitized]) + tx-data (transaction-data {:from-address from-address + :to-address to-address + :token-address token-address + :route route + :data data + :eth-transfer? false})] + {:BridgeName constants/bridge-name-transfer + :ChainID from-chain-id + :TransferTx tx-data})) + +(defn transaction-path + [{:keys [from-address to-address token-id token-address route data eth-transfer?]}] + (let [{:keys [bridge-name amount-in bonder-fees from + to]} route + tx-data (transaction-data {:from-address from-address + :to-address to-address + :token-address token-address + :route route + :data data + :eth-transfer? eth-transfer?}) + to-chain-id (:chain-id to) + from-chain-id (:chain-id from)] + (cond-> {:BridgeName bridge-name + :ChainID from-chain-id} + + (= bridge-name constants/bridge-name-erc-721-transfer) + (assoc :ERC721TransferTx + (assoc tx-data + :Recipient to-address + :TokenID token-id + :ChainID to-chain-id)) + + (= bridge-name constants/bridge-name-erc-1155-transfer) + (assoc :ERC1155TransferTx + (assoc tx-data + :Recipient to-address + :TokenID token-id + :ChainID to-chain-id + :Amount amount-in)) + + (= bridge-name constants/bridge-name-transfer) + (assoc :TransferTx tx-data) + + (= bridge-name constants/bridge-name-hop) + (assoc :HopTx + (assoc tx-data + :ChainID from-chain-id + :ChainIDTo to-chain-id + :Symbol token-id + :Recipient to-address + :Amount amount-in + :BonderFee bonder-fees)) + + (not (or (= bridge-name constants/bridge-name-erc-721-transfer) + (= bridge-name constants/bridge-name-transfer) + (= bridge-name constants/bridge-name-hop))) + (assoc :CbridgeTx + (assoc tx-data + :ChainID to-chain-id + :Symbol token-id + :Recipient to-address + :Amount amount-in))))) + +(defn multi-transaction-command + [{:keys [from-address to-address from-asset to-asset amount-out multi-transaction-type] + :or {multi-transaction-type constants/multi-transaction-type-unknown}}] + {:fromAddress from-address + :toAddress to-address + :fromAsset from-asset + :toAsset to-asset + :fromAmount amount-out + :type multi-transaction-type}) diff --git a/src/status_im/contexts/wallet/send/events.cljs b/src/status_im/contexts/wallet/send/events.cljs index 96ecf61fae..4ea77ffcc3 100644 --- a/src/status_im/contexts/wallet/send/events.cljs +++ b/src/status_im/contexts/wallet/send/events.cljs @@ -11,7 +11,7 @@ [taoensso.timbre :as log] [utils.address :as address] [utils.hex :as utils.hex] - [utils.money :as money] + [utils.money :as utils.money] [utils.number] [utils.re-frame :as rf])) @@ -582,124 +582,6 @@ [{:ms 20 :dispatch [:wallet/clean-up-transaction-flow]}]]]}))) -(defn- transaction-data - [{:keys [from-address to-address token-address route data eth-transfer?]}] - (let [{:keys [amount-in gas-amount gas-fees]} route - eip-1559-enabled? (:eip-1559-enabled gas-fees) - {:keys [gas-price max-fee-per-gas-medium - max-priority-fee-per-gas]} gas-fees] - (cond-> {:From from-address - :To (or token-address to-address) - :Gas (money/to-hex gas-amount) - :Value (when eth-transfer? amount-in) - :Nonce nil - :Input "" - :Data (or data "0x")} - eip-1559-enabled? (assoc - :TxType "0x02" - :MaxFeePerGas - (money/to-hex - (money/->wei - :gwei - max-fee-per-gas-medium)) - :MaxPriorityFeePerGas - (money/to-hex - (money/->wei - :gwei - max-priority-fee-per-gas))) - (not eip-1559-enabled?) (assoc :TxType "0x00" - :GasPrice - (money/to-hex - (money/->wei - :gwei - gas-price)))))) - -(defn- approval-path - [{:keys [route from-address to-address token-address]}] - (let [{:keys [from]} route - from-chain-id (:chain-id from) - approval-amount-required (:approval-amount-required route) - approval-amount-required-sanitized (-> approval-amount-required - (utils.hex/normalize-hex) - (native-module/hex-to-number)) - approval-contract-address (:approval-contract-address route) - data (native-module/encode-function-call - constants/contract-function-signature-erc20-approve - [approval-contract-address - approval-amount-required-sanitized]) - tx-data (transaction-data {:from-address from-address - :to-address to-address - :token-address token-address - :route route - :data data - :eth-transfer? false})] - {:BridgeName constants/bridge-name-transfer - :ChainID from-chain-id - :TransferTx tx-data})) - -(defn- transaction-path - [{:keys [from-address to-address token-id token-address route data eth-transfer?]}] - (let [{:keys [bridge-name amount-in bonder-fees from - to]} route - tx-data (transaction-data {:from-address from-address - :to-address to-address - :token-address token-address - :route route - :data data - :eth-transfer? eth-transfer?}) - to-chain-id (:chain-id to) - from-chain-id (:chain-id from)] - (cond-> {:BridgeName bridge-name - :ChainID from-chain-id} - - (= bridge-name constants/bridge-name-erc-721-transfer) - (assoc :ERC721TransferTx - (assoc tx-data - :Recipient to-address - :TokenID token-id - :ChainID to-chain-id)) - - (= bridge-name constants/bridge-name-erc-1155-transfer) - (assoc :ERC1155TransferTx - (assoc tx-data - :Recipient to-address - :TokenID token-id - :ChainID to-chain-id - :Amount amount-in)) - - (= bridge-name constants/bridge-name-transfer) - (assoc :TransferTx tx-data) - - (= bridge-name constants/bridge-name-hop) - (assoc :HopTx - (assoc tx-data - :ChainID from-chain-id - :ChainIDTo to-chain-id - :Symbol token-id - :Recipient to-address - :Amount amount-in - :BonderFee bonder-fees)) - - (not (or (= bridge-name constants/bridge-name-erc-721-transfer) - (= bridge-name constants/bridge-name-transfer) - (= bridge-name constants/bridge-name-hop))) - (assoc :CbridgeTx - (assoc tx-data - :ChainID to-chain-id - :Symbol token-id - :Recipient to-address - :Amount amount-in))))) - -(defn- multi-transaction-command - [{:keys [from-address to-address from-asset to-asset amount-out multi-transaction-type] - :or {multi-transaction-type constants/multi-transaction-type-unknown}}] - {:fromAddress from-address - :toAddress to-address - :fromAsset from-asset - :toAsset to-asset - :fromAmount amount-out - :type multi-transaction-type}) - (rf/reg-event-fx :wallet/send-transaction (fn [{:keys [db]} [sha3-pwd]] (let [routes (get-in db [:wallet :ui :send :route]) @@ -731,7 +613,7 @@ (native-module/encode-transfer (address/normalized-hex to-address) (:amount-in route))) - base-path (transaction-path + base-path (utils/transaction-path {:to-address to-address :from-address from-address :route route @@ -743,15 +625,15 @@ :data data :eth-transfer? eth-transfer?})] (if approval-required? - [(approval-path {:route route - :token-address token-address - :from-address from-address - :to-address to-address}) + [(utils/approval-path {:route route + :token-address token-address + :from-address from-address + :to-address to-address}) base-path] [base-path])))) routes) request-params - [(multi-transaction-command + [(utils/multi-transaction-command {:from-address from-address :to-address to-address :from-asset token-id diff --git a/src/status_im/contexts/wallet/signals.cljs b/src/status_im/contexts/wallet/signals.cljs index 7409fa2496..99b6a66377 100644 --- a/src/status_im/contexts/wallet/signals.cljs +++ b/src/status_im/contexts/wallet/signals.cljs @@ -1,6 +1,7 @@ (ns status-im.contexts.wallet.signals (:require [oops.core :as oops] + [status-im.constants :as constants] [taoensso.timbre :as log] [utils.re-frame :as rf] [utils.transforms :as transforms])) @@ -8,9 +9,21 @@ (rf/reg-event-fx :wallet/pending-transaction-status-changed-received (fn [{:keys [db]} [{:keys [message]}]] - (let [details (transforms/json->clj message) - tx-hash (:hash details)] - {:db (update-in db [:wallet :transactions tx-hash] assoc :status :confirmed :blocks 1)}))) + (let [details (transforms/json->clj message) + tx-hash (:hash details) + tx-status (:status details) + status (cond + (= tx-status constants/transaction-status-success) + :confirmed + (= tx-status constants/transaction-status-pending) + :pending + (= tx-status constants/transaction-status-failed) + :failed) + swap-approval-transaction-id (get-in db [:wallet :ui :swap :approval-transaction-id]) + swap-approval-transaction? (= swap-approval-transaction-id tx-hash)] + (cond-> {:db (update-in db [:wallet :transactions tx-hash] assoc :status status)} + swap-approval-transaction? + (assoc :fx [[:dispatch [:wallet.swap/approve-transaction-update status]]]))))) (rf/reg-event-fx :wallet/signal-received diff --git a/src/status_im/contexts/wallet/swap/events.cljs b/src/status_im/contexts/wallet/swap/events.cljs index f8eca1454e..3a2cf4be8c 100644 --- a/src/status_im/contexts/wallet/swap/events.cljs +++ b/src/status_im/contexts/wallet/swap/events.cljs @@ -1,9 +1,13 @@ (ns status-im.contexts.wallet.swap.events - (:require [re-frame.core :as rf] + (:require [native-module.core :as native-module] + [re-frame.core :as rf] [status-im.constants :as constants] + [status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.send.utils :as send-utils] [status-im.contexts.wallet.sheets.network-selection.view :as network-selection] [taoensso.timbre :as log] + [utils.address :as address] + [utils.i18n :as i18n] [utils.number])) (rf/reg-event-fx :wallet.swap/start @@ -152,8 +156,123 @@ :last-request-uuid :swap-proposal :error-response - :loading-swap-proposal?)})) + :loading-swap-proposal? + :approval-transaction-id)})) (rf/reg-event-fx :wallet/clean-swap (fn [{:keys [db]}] {:db (update-in db [:wallet :ui] dissoc :swap)})) + +(rf/reg-event-fx :wallet/swap-transaction + (fn [{:keys [db]} [sha3-pwd]] + (let [wallet-address (get-in db [:wallet :current-viewing-account-address]) + {:keys [asset-to-pay swap-proposal network + approval-transaction-id]} (get-in db [:wallet :ui :swap]) + transactions (get-in db [:wallet :transactions]) + approval-transaction (when approval-transaction-id + (get transactions approval-transaction-id)) + already-approved? (and approval-transaction + (= (:status approval-transaction) :confirmed)) + approval-required? (and (:approval-required swap-proposal) + (not already-approved?)) + multi-transaction-type constants/multi-transaction-type-swap + swap-chain-id (:chain-id network) + token-id (:symbol asset-to-pay) + erc20-transfer? (and asset-to-pay (not= token-id "ETH")) + eth-transfer? (and asset-to-pay (not erc20-transfer?)) + token-address (when erc20-transfer? + (get-in asset-to-pay + [:balances-per-chain swap-chain-id :address])) + data (when erc20-transfer? + (native-module/encode-transfer + (address/normalized-hex wallet-address) + (:amount-in swap-proposal))) + transaction-paths (if approval-required? + [(utils/approval-path {:route swap-proposal + :token-address token-address + :from-address wallet-address + :to-address wallet-address})] + [(utils/transaction-path + {:to-address wallet-address + :from-address wallet-address + :route swap-proposal + :token-address token-address + :token-id token-id + :data data + :eth-transfer? eth-transfer?})]) + request-params [(utils/multi-transaction-command + {:from-address wallet-address + :to-address wallet-address + :from-asset token-id + :to-asset token-id + :amount-out (if eth-transfer? + (:amount-out swap-proposal) + "0x0") + :multi-transaction-type multi-transaction-type}) + transaction-paths + sha3-pwd]] + (log/info "multi transaction called") + {:json-rpc/call [{:method "wallet_createMultiTransaction" + :params request-params + :on-success (fn [result] + (when result + (rf/dispatch [:wallet.swap/add-authorized-transaction + {:transaction result + :approval-transaction? approval-required?}]) + (rf/dispatch [:dismiss-modal + :screen/wallet.swap-set-spending-cap]) + (rf/dispatch [:hide-bottom-sheet]))) + :on-error (fn [error] + (log/error "failed swap transaction" + {:event :wallet/swap-transaction + :error error + :params request-params}) + (rf/dispatch [:toasts/upsert + {:id :swap-transaction-error + :type :negative + :text (:message error)}]))}]}))) + +(rf/reg-event-fx :wallet.swap/add-authorized-transaction + (fn [{:keys [db]} [{:keys [transaction approval-transaction?]}]] + (let [transaction-batch-id (:id transaction) + transaction-hashes (:hashes transaction) + transaction-ids (flatten (vals transaction-hashes)) + transaction-details (send-utils/map-multitransaction-by-ids transaction-batch-id + transaction-hashes)] + {:db (cond-> db + :always (assoc-in [:wallet :transactions] transaction-details) + :always (assoc-in [:wallet :ui :swap :transaction-ids] transaction-ids) + approval-transaction? (assoc-in [:wallet :ui :swap :approval-transaction-id] + (first transaction-ids)))}))) + +(rf/reg-event-fx :wallet.swap/approve-transaction-update + (fn [{:keys [db]} [status]] + (let [{:keys [amount asset-to-pay swap-proposal]} (get-in db [:wallet :ui :swap]) + provider-name (:bridge-name swap-proposal) + token-symbol (:symbol asset-to-pay) + current-viewing-account-address (get-in db + [:wallet :current-viewing-account-address]) + account-name (get-in db + [:wallet :accounts + current-viewing-account-address :name]) + transaction-confirmed-or-failed? (#{:confirmed :failed} status) + transaction-confirmed? (= status :confirmed)] + (when transaction-confirmed-or-failed? + (cond-> {:fx + [[:dispatch + [:toasts/upsert + {:id :approve-transaction-update + :type (if transaction-confirmed? :positive :negative) + :text (if transaction-confirmed? + (i18n/label :t/spending-cap-set + {:amount amount + :token-symbol token-symbol + :provider-name provider-name + :account-name account-name}) + (i18n/label :t/spending-cap-failed + {:amount amount + :token-symbol token-symbol + :provider-name provider-name + :account-name account-name}))}]]]} + (not transaction-confirmed?) + (assoc :db (update-in db [:wallet :ui :swap] dissoc :approval-transaction-id))))))) diff --git a/src/status_im/contexts/wallet/swap/set_spending_cap/view.cljs b/src/status_im/contexts/wallet/swap/set_spending_cap/view.cljs index ed13970d6d..acf7859f31 100644 --- a/src/status_im/contexts/wallet/swap/set_spending_cap/view.cljs +++ b/src/status_im/contexts/wallet/swap/set_spending_cap/view.cljs @@ -1,5 +1,6 @@ (ns status-im.contexts.wallet.swap.set-spending-cap.view (:require + [native-module.core :as native-module] [quo.core :as quo] [quo.foundations.resources :as resources] [quo.theme :as quo.theme] @@ -7,84 +8,123 @@ [status-im.common.events-helper :as events-helper] [status-im.common.floating-button-page.view :as floating-button-page] [status-im.common.standard-authentication.core :as standard-auth] + [status-im.constants :as constants] [status-im.contexts.wallet.common.utils.external-links :as external-links] [status-im.contexts.wallet.swap.set-spending-cap.style :as style] [utils.address :as address-utils] + [utils.hex :as hex] [utils.i18n :as i18n] - [utils.re-frame :as rf])) + [utils.number :as number] + [utils.re-frame :as rf] + [utils.security.core :as security])) (defn- swap-title - [{:keys [pay-token-symbol pay-amount account provider]}] - [rn/view {:style style/content-container} - [rn/view {:style {:flex-direction :row}} - [quo/text - {:size :heading-1 - :weight :semi-bold - :style style/title-container - :accessibility-label :set-spending-cap-of} - (i18n/label :t/set-spending-cap-of)]] - [rn/view {:style style/title-line-with-margin-top} - [quo/summary-tag - {:token pay-token-symbol - :label (str pay-amount " " pay-token-symbol) - :type :token}] - [quo/text - {:size :heading-1 - :weight :semi-bold - :style style/title-container - :accessibility-label :for} - (i18n/label :t/for)]] - [rn/view {:style style/title-line-with-margin-top} - [quo/summary-tag - {:label (:full-name provider) - :type :network - :image-source (resources/get-network (:name provider)) - :customization-color (:color provider)}] - [quo/text - {:size :heading-1 - :weight :semi-bold - :style style/title-container - :accessibility-label :on} - (i18n/label :t/on)]] - [rn/view {:style style/title-line-with-margin-top} - [quo/summary-tag - {:label (:name account) - :type :account - :emoji (:emoji account) - :customization-color (:color account)}]]]) + [] + (let [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]) + provider (rf/sub [:wallet/swap-proposal-provider]) + 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 {:flex-direction :row}} + [quo/text + {:size :heading-1 + :weight :semi-bold + :style style/title-container + :accessibility-label :set-spending-cap-of} + (i18n/label :t/set-spending-cap-of)]] + [rn/view {:style style/title-line-with-margin-top} + [quo/summary-tag + {:token pay-token-symbol + :label (str pay-amount " " pay-token-symbol) + :type :token}] + [quo/text + {:size :heading-1 + :weight :semi-bold + :style style/title-container + :accessibility-label :for} + (i18n/label :t/for)]] + [rn/view {:style style/title-line-with-margin-top} + [quo/summary-tag + {:label (:full-name provider) + :type :network + :image-source (resources/get-network (:name provider)) + :customization-color (:color provider)}] + [quo/text + {:size :heading-1 + :weight :semi-bold + :style style/title-container + :accessibility-label :on} + (i18n/label :t/on)]] + [rn/view {:style style/title-line-with-margin-top} + [quo/summary-tag + {:label (:name account) + :type :account + :emoji (:emoji account) + :customization-color (:color account)}]]])) (defn- spending-cap-section - [{:keys [theme amount token-symbol]}] - [rn/view {:style style/summary-section-container} - [quo/text - {:size :paragraph-2 - :weight :medium - :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 amount " " token-symbol) - :avatar-props {:token token-symbol}}]]) + [] + (let [theme (quo.theme/use-theme) + asset-to-pay (rf/sub [:wallet/swap-asset-to-pay]) + amount-in (rf/sub [:wallet/swap-proposal-amount-in]) + 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} + [quo/text + {:size :paragraph-2 + :weight :medium + :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}}]])) (defn- account-section - [{:keys [theme account pay-token-symbol pay-token-amount]}] - [rn/view {:style style/summary-section-container} - [quo/text - {:size :paragraph-2 - :weight :medium - :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-token-amount " " pay-token-symbol) - :avatar-props {:emoji (:emoji account) - :customization-color (:color account)}}]]) + [] + (let [theme (quo.theme/use-theme) + 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]) + 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} + [quo/text + {:size :paragraph-2 + :weight :medium + :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)}}]])) (defn- on-option-press [{:keys [chain-id contract-address]}] @@ -103,42 +143,52 @@ :right-icon :i/external}]]])}])) (defn- token-section - [{:keys [theme token-address token-symbol network-chain-id]}] - [rn/view {:style style/summary-section-container} - [quo/text - {:size :paragraph-2 - :weight :medium - :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 token-address}) - :unlimited-icon? false - :label token-symbol - :description (address-utils/get-short-wallet-address token-address) - :avatar-props {:token token-symbol}}]]) + [] + (let [theme (quo.theme/use-theme) + asset-to-pay (rf/sub [:wallet/swap-asset-to-pay]) + network (rf/sub [:wallet/swap-network]) + pay-token-symbol (:symbol asset-to-pay) + network-chain-id (:chain-id network) + pay-token-address (get-in asset-to-pay [:balances-per-chain network-chain-id :address])] + [rn/view {:style style/summary-section-container} + [quo/text + {:size :paragraph-2 + :weight :medium + :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}}]])) (defn- spender-contract-section - [{:keys [theme provider network-chain-id]}] - [rn/view {:style style/summary-section-container} - [quo/text - {:size :paragraph-2 - :weight :medium - :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))}}]]) + [] + (let [theme (quo.theme/use-theme) + network (rf/sub [:wallet/swap-network]) + provider (rf/sub [:wallet/swap-proposal-provider]) + network-chain-id (:chain-id network)] + [rn/view {:style style/summary-section-container} + [quo/text + {:size :paragraph-2 + :weight :medium + :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))}}]])) (defn- data-item [{:keys [network-image title subtitle size loading?]}] @@ -154,56 +204,52 @@ :size size}]) (defn- transaction-details - [{:keys [estimated-time-min max-fees network loading-fees?]}] - [rn/view {:style style/details-container} - [:<> - [data-item - {:title (i18n/label :t/network) - :subtitle (:full-name network) - :network-image (:source network)}] - [data-item - {:title (i18n/label :t/est-time) - :subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time-min)})}] - [data-item - {:title (i18n/label :t/max-fees) - :subtitle max-fees - :loading? loading-fees? - :size :small}]]]) + [] + (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])] + [rn/view {:style style/details-container} + [:<> + [data-item + {:title (i18n/label :t/network) + :subtitle (:full-name 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 + {:title (i18n/label :t/max-fees) + :subtitle max-fees + :loading? loading-fees? + :size :small}]]])) -(defn footer - [{:keys [estimated-time-min native-currency-symbol network theme account-color loading-fees?]}] - (let [fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol]) - on-auth-success (rn/use-callback #(js/alert "Not implemented yet"))] - [rn/view {:style {:margin-bottom -10}} - [transaction-details - {:estimated-time-min estimated-time-min - :max-fees fee-formatted - :network network - :loading-fees? loading-fees? - :theme theme}] - [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? - :on-auth-success on-auth-success - :auth-button-label (i18n/label :t/confirm)}]])) +(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 %)]))] + [standard-auth/slide-button + {:size :size-48 + :track-text (i18n/label :t/slide-to-swap) + :container-style {:z-index 2} + :customization-color (:color account) + :disabled? loading-fees? + :on-auth-success on-auth-success + :auth-button-label (i18n/label :t/confirm)}])) + +(defn- footer + [] + [rn/view {:style {:margin-bottom -10}} + [transaction-details] + [slide-button]]) (defn view [] - (let [theme (quo.theme/use-theme) - swap-transaction-data (rf/sub [:wallet/swap]) - {:keys [asset-to-pay network pay-amount - providers swap-proposal - loading-fees?]} swap-transaction-data - 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)] + (let [account (rf/sub [:wallet/current-viewing-account])] [rn/view {:style style/container} [floating-button-page/view {:footer-container-padding 0 @@ -213,37 +259,12 @@ :margin-top 8 :background :blur :accessibility-label :top-bar}] - :footer [footer - {:estimated-time-min estimated-time-min - :native-currency-symbol native-currency-symbol - :network network - :account-color account-color - :provider provider - :loading-fees? loading-fees? - :theme theme}] + :footer [footer] :gradient-cover? true - :customization-color account-color} + :customization-color (:color account)} [:<> - [swap-title - {:pay-token-symbol pay-token-symbol - :pay-amount pay-amount - :account account - :provider provider}] - [spending-cap-section - {:token-symbol pay-token-symbol - :amount pay-amount - :theme theme}] - [account-section - {:account account - :pay-token-symbol pay-token-symbol - :pay-token-amount pay-amount - :theme theme}] - [token-section - {:token-symbol pay-token-symbol - :token-address pay-token-address - :network-chain-id (:chain-id network) - :theme theme}] - [spender-contract-section - {:provider provider - :network-chain-id (:chain-id network) - :theme theme}]]]])) + [swap-title] + [spending-cap-section] + [account-section] + [token-section] + [spender-contract-section]]]])) diff --git a/src/status_im/contexts/wallet/swap/setup_swap/view.cljs b/src/status_im/contexts/wallet/swap/setup_swap/view.cljs index 61ead58004..32546012ec 100644 --- a/src/status_im/contexts/wallet/swap/setup_swap/view.cljs +++ b/src/status_im/contexts/wallet/swap/setup_swap/view.cljs @@ -76,6 +76,7 @@ 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]) + approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status]) 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) @@ -141,7 +142,11 @@ {:number available-crypto-limit :token-symbol pay-token-symbol}) :networks [{:source (:source network)}]} - :approval-label-props {:status :approve + :approval-label-props {:status (case approval-transaction-status + :pending :approving + :confirmed :approved + :finalised :approved + :approve) :token-value approval-amount-required-num :button-props {:on-press on-approve-press} :customization-color account-color @@ -199,16 +204,18 @@ (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])] + (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])] [quo/bottom-actions {:actions :one-action :button-one-label (i18n/label :t/review-swap) :button-one-props {:disabled? (or (not swap-proposal) - approval-required? + (and approval-required? + (not= approval-transaction-status :confirmed)) loading-swap-proposal? loading-fees?) :customization-color account-color @@ -262,7 +269,7 @@ :on-max-press #(set-pay-input-state pay-token-balance-selected-chain) :input-focused? pay-input-focused? :on-token-press #(js/alert "Token Pressed") - :on-approve-press #(js/alert "Approve Pressed") + :on-approve-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap]) :on-input-focus #(set-pay-input-focused? true)}] [swap-order-button {:on-press #(js/alert "Swap Order Pressed")}] [receive-token-input diff --git a/src/status_im/subs/wallet/swap.cljs b/src/status_im/subs/wallet/swap.cljs index c8f013ba08..02bdc9075d 100644 --- a/src/status_im/subs/wallet/swap.cljs +++ b/src/status_im/subs/wallet/swap.cljs @@ -80,6 +80,18 @@ :<- [:wallet/swap] :-> :loading-fees?) +(rf/reg-sub + :wallet/swap-approval-transaction-id + :<- [:wallet/swap] + :-> :approval-transaction-id) + +(rf/reg-sub + :wallet/swap-approval-transaction-status + :<- [:wallet/transactions] + :<- [:wallet/swap-approval-transaction-id] + (fn [[transactions approval-transaction-id]] + (get-in transactions [approval-transaction-id :status]))) + (rf/reg-sub :wallet/swap-proposal :<- [:wallet/swap] @@ -95,6 +107,19 @@ :<- [:wallet/swap-proposal] :-> :amount-out) +(rf/reg-sub + :wallet/swap-proposal-amount-in + :<- [:wallet/swap-proposal] + :-> :amount-in) + +(rf/reg-sub + :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)))) + (rf/reg-sub :wallet/swap-proposal-approval-required :<- [:wallet/swap-proposal] @@ -105,6 +130,11 @@ :<- [:wallet/swap-proposal] :-> :approval-amount-required) +(rf/reg-sub + :wallet/swap-proposal-estimated-time + :<- [:wallet/swap-proposal] + :-> :estimated-time) + (rf/reg-sub :wallet/wallet-swap-proposal-fee-fiat-formatted :<- [:wallet/current-viewing-account] diff --git a/translations/en.json b/translations/en.json index 28bd3d71ed..26d6e71a74 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2313,6 +2313,8 @@ "specify-symbol": "Specify a symbol", "spender-contract": "Spender contract", "spending-cap": "Spending cap", + "spending-cap-failed": "Spending cap failed: {{amount}} {{token-symbol}} for {{provider-name}} in {{account-name}}", + "spending-cap-set": "Spending cap set: {{amount}} {{token-symbol}} for {{provider-name}} in {{account-name}}", "start-chat": "Start chat", "start-conversation": "Start conversation", "start-group-chat": "Start group chat",