diff --git a/src/status_im/common/signals/events.cljs b/src/status_im/common/signals/events.cljs index 5ea412bf352..a84f86b97bc 100644 --- a/src/status_im/common/signals/events.cljs +++ b/src/status_im/common/signals/events.cljs @@ -39,12 +39,19 @@ "wallet.suggested.routes" {:fx [[:dispatch [:wallet/handle-suggested-routes (transforms/js->clj event-js)]]]} + "wallet.router.sending-transactions-started" + {:fx [[:dispatch + [:wallet/sending-transactions-started-signal-received (transforms/js->clj event-js)]]]} + "wallet.router.sign-transactions" {:fx [[:dispatch [:wallet/sign-transactions-signal-received (transforms/js->clj event-js)]]]} "wallet.router.transactions-sent" {:fx [[:dispatch [:wallet/transactions-sent-signal-received (transforms/js->clj event-js)]]]} + "wallet.transaction.status-changed" + {:fx [[:dispatch [:wallet/status-changed-signal-received (transforms/js->clj event-js)]]]} + "envelope.sent" (messages.transport/update-envelopes-status cofx diff --git a/src/status_im/contexts/wallet/common/utils.cljs b/src/status_im/contexts/wallet/common/utils.cljs index f42dc8e880d..674cc5575b3 100644 --- a/src/status_im/contexts/wallet/common/utils.cljs +++ b/src/status_im/contexts/wallet/common/utils.cljs @@ -452,6 +452,89 @@ (some positive-balance-in-any-chain? tokens)) operable-account)))) +(defn send-details-map + "Generates a new map with all the sen details using the original + details we've received from `status-go`. The new one contains only + the keys we need." + [send-details] + (let [{:keys [sendType + fromAddress + toAddress + fromChain + toChain + fromAmount + toAmount + fromAsset + toAsset + username + publicKey + packId]} send-details] + {:uuid (:uuid send-details) + :send-type sendType + :address-from fromAddress + :address-to toAddress + :tx-to "" + :from-chain fromChain + :to-chain toChain + :from-amount fromAmount + :to-amount toAmount + :from-asset fromAsset + :to-asset toAsset + :username username + :public-key publicKey + :pack-id packId + :tx-hash "" + :approval-tx? false})) + +(defn details-map + "Generates map with all the transaction details based on what we've + got from status-go as `send-details` and `sent-transaction`." + [send-details sent-transaction] + (let [send-details (send-details-map send-details) + {:keys [toAddress fromChain toChain amountIn + amountOut fromToken toToken + approvalTx]} sent-transaction + amount-in (money/from-hex amountIn) + amount-out (money/from-hex amountOut) + sent-transaction? (and sent-transaction (> (-> sent-transaction :hash count) 0))] + (if sent-transaction? + (cond-> send-details + :always (assoc :tx-to toAddress + :tx-hash (:hash sent-transaction) + :approval-tx? approvalTx) + (> fromChain 0) (assoc :from-chain fromChain) + (> toChain 0) (assoc :to-chain toChain) + (not= amount-in "0") (assoc :from-amount amount-in) + (not= amount-out "0") (assoc :to-amount amount-out) + (> (count fromToken) 0) (assoc :from-asset fromToken) + (> (count toToken) 0) (assoc :to-asset toToken)) + send-details))) + +(defn contact-name-by-address + [db address] + (or (get-in db [:wallet :accounts address :name]) + (get-in db [:contacts/contacts address :primary-name]))) + +(defn tx-to-name + "Returns the transaction name that will be used for certain types of transactions." + [send-type] + (cond + (= send-type constants/send-type-bridge) constants/bridge-name-hop + (= send-type constants/send-type-swap) (:full-name constants/swap-provider-paraswap) + :else nil)) + +(defn transaction-approval-required? + "Indicates whether the transaction needs approval based on the information + from the database." + [transactions {:keys [swap-proposal approval-transaction-id]}] + (let [approval-transaction (when approval-transaction-id + (get transactions approval-transaction-id)) + already-approved? (and approval-transaction + (= (:status approval-transaction) + :confirmed))] + (and (:approval-required swap-proposal) + (not already-approved?)))) + (defn on-paste-address-or-ens "Check if the clipboard has any valid address and extract the address without any chain info. If it does not contain an valid address or it is ENS, return the clipboard text as it is" diff --git a/src/status_im/contexts/wallet/events.cljs b/src/status_im/contexts/wallet/events.cljs index 79d06ad7a8f..961fd48f7d2 100644 --- a/src/status_im/contexts/wallet/events.cljs +++ b/src/status_im/contexts/wallet/events.cljs @@ -777,22 +777,74 @@ (rf/reg-event-fx :wallet/transactions-sent-signal-received - (fn [{:keys [db]} + (fn [_ + [{sent-transactions :sentTransactions + send-details :sendDetails}]] + {:fx [[:dispatch + [:wallet/show-transaction-notification + {:status :sent + :send-details send-details + :sent-transaction (first sent-transactions)}]] + [:dispatch + (if (:errorResponse send-details) + [:wallet/transaction-failure send-details] + [(if (= (:sendType send-details) constants/send-type-swap) + :wallet.swap/transaction-success + :wallet/transaction-success) + sent-transactions])]]})) + +(rf/reg-event-fx + :wallet/sending-transactions-started-signal-received + (fn [_ [{sent-transactions :sentTransactions send-details :sendDetails}]] - (let [swap? (-> db - (get-in db-path/swap) - seq)] - {:fx [[:dispatch - (if-let [error-response (:errorResponse send-details)] - [(if swap? - :wallet.swap/transaction-failure - :wallet/transaction-failure) - error-response] - [(if swap? - :wallet.swap/transaction-success - :wallet/transaction-success) - sent-transactions])]]}))) + {:fx [[:dispatch + [:wallet/show-transaction-notification + {:status :sending + :send-details send-details + :sent-transaction (first sent-transactions)}]]]})) + +(rf/reg-event-fx + :wallet/status-changed-signal-received + (fn [_ + [{sent-transactions :sentTransactions + send-details :sendDetails}]] + {:fx [[:dispatch + [:wallet/show-transaction-notification + {:status :status-changed + :send-details send-details + :sent-transaction (first sent-transactions)}]]]})) + +(rf/reg-event-fx + :wallet/show-transaction-notification + (fn [{:keys [db]} [{:keys [status send-details sent-transaction]}]] + (let [{:keys [error send-type] + :as details} + (as-> (utils/details-map send-details sent-transaction) $ + (assoc $ :error (get-in send-details [:ErrorResponse :details])) + (assoc $ :account-from-name (utils/contact-name-by-address db (:address-from $))) + (assoc $ :account-to-name (utils/contact-name-by-address db (:address-to $))) + (assoc $ + :tx-to-name + (or (utils/tx-to-name (:send-type $)) + (utils/contact-name-by-address db (:tx-to $)))))] + (cond + ;; handle errors and show notifications about errors + error + (do + (log/warn "Error when sending transaction" details) + {:fx [[:dispatch + [:toasts/upsert + {:id (keyword (str status "-failure")) + :type :negative + :text error}]]]}) + + ;; handle swap notifications in a separate fx + (= send-type constants/send-type-swap) + {:fx [[:dispatch + [:wallet.swap/show-transaction-notification + {:status status + :send-details details}]]]})))) (rf/reg-event-fx :wallet/set-max-base-fee @@ -884,4 +936,3 @@ (rf/reg-event-fx :wallet/mark-user-tx-settings-for-deletion (fn [{db :db}] {:db (assoc-in db [:wallet :ui :user-tx-settings :delete-on-routes-update?] true)})) - diff --git a/src/status_im/contexts/wallet/send/events.cljs b/src/status_im/contexts/wallet/send/events.cljs index de25c14f52d..4db505978ad 100644 --- a/src/status_im/contexts/wallet/send/events.cljs +++ b/src/status_im/contexts/wallet/send/events.cljs @@ -567,49 +567,46 @@ token-decimal (when token (:decimals token)) token-id (utils/format-token-id token collectible) to-token-id "" - gas-rates constants/gas-rate-medium - to-hex (fn [v] - (send-utils/amount-in-hex v (if token token-decimal 0))) - amount-in (to-hex amount) - amount-out (to-hex amount-out) - from-address wallet-address - network-chain-id (if collectible - (get-in collectible [:id :contract-id :chain-id]) - (:chain-id network)) - disabled-from-chain-ids (filter #(not= % network-chain-id) network-chain-ids) - disabled-to-chain-ids (filter #(not= % - (if (= tx-type :tx/bridge) - bridge-to-chain-id - network-chain-id)) - network-chain-ids) - send-type (case tx-type - :tx/collectible-erc-721 - constants/send-type-erc-721-transfer - :tx/collectible-erc-1155 - constants/send-type-erc-1155-transfer - :tx/bridge constants/send-type-bridge - constants/send-type-transfer) - sender-network-values (when (= tx-type :tx/bridge) - (send-utils/loading-network-amounts - {:networks [network-chain-id] - :values {network-chain-id amount} - :receiver? false})) - receiver-network-values (when (= tx-type :tx/bridge) - (send-utils/loading-network-amounts - {:networks [bridge-to-chain-id] - :receiver? true})) - request-uuid (str (random-uuid)) - params [{:uuid request-uuid - :sendType send-type - :addrFrom from-address - :addrTo to-address - :amountIn amount-in - :amountOut amount-out - :tokenID token-id - :toTokenID to-token-id + to-hex (fn [v] (send-utils/amount-in-hex v (if token token-decimal 0))) + amount-in (to-hex amount) + amount-out (to-hex amount-out) + from-address wallet-address + network-chain-id (if collectible + (get-in collectible [:id :contract-id :chain-id]) + (:chain-id network)) + disabled-from-chain-ids (filter #(not= % network-chain-id) network-chain-ids) + disabled-to-chain-ids (filter #(not= % + (if (= tx-type :tx/bridge) + bridge-to-chain-id + network-chain-id)) + network-chain-ids) + send-type (case tx-type + :tx/collectible-erc-721 constants/send-type-erc-721-transfer + :tx/collectible-erc-1155 constants/send-type-erc-1155-transfer + :tx/bridge constants/send-type-bridge + constants/send-type-transfer) + sender-network-values (when (= tx-type :tx/bridge) + (send-utils/loading-network-amounts + {:networks [network-chain-id] + :values {network-chain-id amount} + :receiver? false})) + receiver-network-values (when (= tx-type :tx/bridge) + (send-utils/loading-network-amounts + {:networks [bridge-to-chain-id] + :receiver? true})) + request-uuid (str (random-uuid)) + params [{:uuid request-uuid + :sendType send-type + :addrFrom from-address + :addrTo to-address + :amountIn amount-in + :amountOut amount-out + :tokenID token-id + :tokenIDIsOwnerToken false + :toTokenID to-token-id :disabledFromChainIDs disabled-from-chain-ids :disabledToChainIDs disabled-to-chain-ids - :gasFeeMode gas-rates + :gasFeeMode constants/gas-rate-medium :fromLockedAmount {} :username (:username args) :publicKey (:publicKey args) @@ -651,16 +648,16 @@ (rf/reg-event-fx :wallet/handle-suggested-routes (fn [{:keys [db]} [data]] - (let [{send :send swap? :swap} (-> db :wallet :ui) - skip-processing-routes? (:skip-processing-suggested-routes? send) - clean-user-tx-settings? (get-in db - [:wallet :ui :user-tx-settings - :delete-on-routes-update?])] - (when (or swap? (not skip-processing-routes?)) + (let [{:keys [send swap]} (-> db :wallet :ui) + skip-processing-routes? (:skip-processing-suggested-routes? send) + clean-user-tx-settings? (get-in db + [:wallet :ui :user-tx-settings + :delete-on-routes-update?])] + (when (or swap (not skip-processing-routes?)) (let [{error-code :code :as error} (:ErrorResponse data) enough-assets? (not (and (:Best data) (= error-code "WR-002"))) - failure? (and error enough-assets? (not swap?)) + failure? (and error enough-assets? (not swap)) error-message (if (zero? error-code) "An error occurred" (:details error))] (when failure? (log/error "failed to get suggested routes (async)" @@ -671,11 +668,11 @@ {:db (update-in db [:wallet :ui] dissoc :user-tx-settings)}) {:fx [[:dispatch (cond - (and failure? swap?) [:wallet/swap-proposal-error error] - failure? [:wallet/suggested-routes-error error-message] - swap? [:wallet/swap-proposal-success (data-store/fix-routes data)] - :else [:wallet/suggested-routes-success (data-store/fix-routes data) - enough-assets?])]]})))))) + (and failure? swap) [:wallet/swap-proposal-error error] + failure? [:wallet/suggested-routes-error error-message] + swap [:wallet/swap-proposal-success (data-store/fix-routes data)] + :else [:wallet/suggested-routes-success (data-store/fix-routes data) + enough-assets?])]]})))))) (rf/reg-event-fx :wallet/transaction-success @@ -701,16 +698,14 @@ (rf/reg-event-fx :wallet/transaction-failure - (fn [_ [{:keys [details]}]] - {:fx [[:dispatch [:wallet/end-transaction-flow]] - [:dispatch-later - [{:ms 2000 - :dispatch [:wallet/stop-and-clean-suggested-routes]}]] - [:dispatch - [:toasts/upsert - {:id :send-transaction-failure - :type :negative - :text (or details "An error occured")}]]]})) + (fn [_ [send-details]] + (if (= (:sendType send-details) constants/send-type-swap) + {:fx [[:dispatch [:wallet.swap/track-transaction-execution-failed (:errorResponse send-details)]] + [:dispatch [:wallet.swap/end-transaction-flow]]]} + {:fx [[:dispatch [:wallet/end-transaction-flow]] + [:dispatch-later + [{:ms 2000 + :dispatch [:wallet/stop-and-clean-suggested-routes]}]]]}))) (rf/reg-event-fx :wallet/clean-just-completed-transaction (fn [{:keys [db]}] diff --git a/src/status_im/contexts/wallet/swap/events.cljs b/src/status_im/contexts/wallet/swap/events.cljs index b3437537c93..14bea2aabb6 100644 --- a/src/status_im/contexts/wallet/swap/events.cljs +++ b/src/status_im/contexts/wallet/swap/events.cljs @@ -468,16 +468,6 @@ [:wallet.swap/set-sign-transactions-callback-fx [:dispatch [:wallet/prepare-signatures-for-transactions :swap]]]]]}))) -(defn transaction-approval-required? - [transactions {:keys [swap-proposal approval-transaction-id]}] - (let [approval-transaction (when approval-transaction-id - (get transactions approval-transaction-id)) - already-approved? (and approval-transaction - (= (:status approval-transaction) - :confirmed))] - (and (:approval-required swap-proposal) - (not already-approved?)))) - (rf/reg-event-fx :wallet.swap/mark-as-pending (fn [{:keys [db]} [transaction-id]] @@ -504,7 +494,7 @@ (-> amount-out (number/hex->whole receive-token-decimals) (money/to-fixed receive-token-decimals))) - approval-required? (transaction-approval-required? transactions swap)] + approval-required? (utils/transaction-approval-required? transactions swap)] {:fx [[:dispatch [:centralized-metrics/track (if approval-required? @@ -532,23 +522,11 @@ [:dispatch [:wallet.swap/mark-as-pending (-> sent-transactions first :hash)]]) (when-not approval-required? ;; just end the whole transaction flow if no approval needed - [:dispatch [:wallet.swap/end-transaction-flow]]) - (when-not approval-required? - [:dispatch-later - {:ms 500 - :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})}]}])]}))) + [:dispatch [:wallet.swap/end-transaction-flow]])]}))) (rf/reg-event-fx - :wallet.swap/transaction-failure - (fn [{:keys [db]} [{:keys [details] :as error}]] + :wallet.swap/track-transaction-execution-failed + (fn [{:keys [db]} [error]] (let [transactions (get-in db [:wallet :transactions]) {:keys [asset-to-pay asset-to-receive @@ -557,7 +535,7 @@ swap-chain-id (:chain-id network) token-id-from (:symbol asset-to-pay) token-id-to (:symbol asset-to-receive) - approval-required? (transaction-approval-required? transactions swap)] + approval-required? (utils/transaction-approval-required? transactions swap)] {:fx [[:centralized-metrics/track (if approval-required? :metric/swap-approval-execution-failed @@ -566,20 +544,14 @@ :error error :pay_token token-id-from} (not approval-required?) - (assoc :receive_token token-id-to))] - [:dispatch [:wallet.swap/end-transaction-flow]] - [:dispatch - [:toasts/upsert - {:id :send-transaction-error - :type :negative - :text (or details "An error occured")}]]]}))) + (assoc :receive_token token-id-to))]]}))) (rf/reg-event-fx :wallet.swap/clean-up-transaction-flow (fn [{:keys [db]}] (let [transactions (get-in db [:wallet :transactions]) swap (get-in db db-path/swap) - approval-required? (transaction-approval-required? transactions swap)] + approval-required? (utils/transaction-approval-required? transactions swap)] {:db (update-in db [:wallet :ui] dissoc :swap) :fx [[:dispatch [:dismiss-modal @@ -613,6 +585,32 @@ [:navigate-to-within-stack [:screen/wallet.swap-select-asset-to-pay :screen/wallet.swap-select-account]]]])}))) +(rf/reg-event-fx + :wallet.swap/show-transaction-notification + (fn [{:keys [db]} [{:keys [status send-details]}]] + (let [transactions (get-in db [:wallet :transactions]) + {:keys [asset-to-pay asset-to-receive] :as swap} (get-in db [:wallet :ui :swap])] + ;; show toast when approval is not required + (when (and (= status :sent) + (not (utils/transaction-approval-required? transactions swap))) + {:fx [[:dispatch-later + {:ms 500 + :dispatch [:toasts/upsert + {:id :swap-transaction-pending + :icon :i/info + :type :neutral + :text (i18n/label :t/swapping-to + {:pay-amount (-> send-details + :from-amount + (money/token->unit + (:decimals asset-to-pay))) + :receive-amount (-> send-details + :to-amount + (money/token->unit + (:decimals asset-to-receive))) + :pay-token-symbol (:from-asset send-details) + :receive-token-symbol (:to-asset send-details)})}]}]]})))) + (rf/reg-event-fx :wallet/get-swap-proposal-fee (fn [{:keys [db]} [{:keys [amount-in amount-out]}]] (let [request-uuid (str (random-uuid))