Wallet Connect transactions (#20755)
* feat: updated signing endpoints and refactor6e056348...e8aec741
* fix: using the generic warning at the bottom * fix: show parsed transaction parameters * feat: adding fees to transactions6e056348...b2e5e7a8
* feat: added fees * feat: added eip-1559 fee estimation & tx priority * feat: added fees and failed processing handling * fix: show testnet name in the request * fix: address review comments * feat: added max-fee color when not enough balance * ref: broke down tx fees subscription * fix: handle gas estimation on status-go1ef2434b...5389f281
* fix: don't overwrite dynamic fees if already there * fix: malli schema and review comments * fix: addressed review comments 1 * fix: addressed comments 2 * fix: removed unused require * fix: addressed QA review484b8aca...d07f9b5b
* fix: requests being shown simultaneously * fix: removed support for eth_signTransaction
This commit is contained in:
parent
84b8943fd4
commit
02e24208db
|
@ -121,6 +121,7 @@
|
|||
[:card? {:optional true} [:maybe :boolean]]
|
||||
[:right-icon {:optional true} [:maybe :keyword]]
|
||||
[:right-content {:optional true} [:maybe :map]]
|
||||
[:icon-color {:optional true} [:maybe :schema.common/customization-color]]
|
||||
[:status {:optional true} [:maybe [:enum :default :loading]]]
|
||||
[:subtitle-type {:optional true} [:maybe [:enum :default :icon :network :account :editable]]]
|
||||
[:size {:optional true} [:maybe [:enum :default :small :large]]]
|
||||
|
|
|
@ -281,7 +281,8 @@
|
|||
#{wallet-connect-personal-sign-method
|
||||
wallet-connect-eth-sign-method
|
||||
wallet-connect-eth-send-transaction-method
|
||||
wallet-connect-eth-sign-transaction-method
|
||||
;; NOTE: disabled, as we have no clear use cases for it and other wallets don't support it
|
||||
;; wallet-connect-eth-sign-transaction-method
|
||||
wallet-connect-eth-sign-typed-method
|
||||
wallet-connect-eth-sign-typed-v4-method})
|
||||
(def ^:const wallet-connect-supported-events #{"accountsChanged" "chainChanged"})
|
||||
|
@ -512,6 +513,9 @@
|
|||
(def ^:const optimism-full-name "Optimism")
|
||||
(def ^:const arbitrum-full-name "Arbitrum")
|
||||
|
||||
(def ^:const sepolia-full-name "Sepolia")
|
||||
(def ^:const goerli-full-name "Goerli")
|
||||
|
||||
(def ^:const mainnet-network-name :mainnet)
|
||||
(def ^:const ethereum-network-name :ethereum)
|
||||
(def ^:const optimism-network-name :optimism)
|
||||
|
|
|
@ -172,7 +172,8 @@
|
|||
|
||||
(defn get-network-details
|
||||
[chain-id]
|
||||
(condp contains? chain-id
|
||||
(as-> chain-id $
|
||||
(condp contains? $
|
||||
#{constants/ethereum-mainnet-chain-id constants/ethereum-goerli-chain-id
|
||||
constants/ethereum-sepolia-chain-id}
|
||||
mainnet-network-details
|
||||
|
@ -185,4 +186,6 @@
|
|||
constants/optimism-sepolia-chain-id}
|
||||
optimism-network-details
|
||||
|
||||
nil))
|
||||
nil)
|
||||
(when $
|
||||
(assoc $ :chain-id chain-id))))
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
[]
|
||||
[quo/alert-banner
|
||||
{:action? true
|
||||
:text (i18n/label :t/not-enough-assets)
|
||||
:text (i18n/label :t/not-enough-assets-to-pay-gas-fees)
|
||||
:button-text (i18n/label :t/buy-eth)
|
||||
:on-button-press #(rf/dispatch [:show-bottom-sheet
|
||||
{:content buy-token/view}])}])
|
||||
|
|
|
@ -100,6 +100,17 @@
|
|||
networks (get-in db [:wallet :networks (if test-mode? :test :prod)])]
|
||||
(mapv #(-> % :chain-id) networks)))
|
||||
|
||||
(defn add-full-testnet-name
|
||||
"Updates the `:full-name` key with the full testnet name if using testnet `:chain-id`.\n
|
||||
e.g. `{:full-name \"Mainnet\"}` -> `{:full-name \"Mainnet Sepolia\"`}`"
|
||||
[network]
|
||||
(let [add-testnet-name (fn [testnet-name]
|
||||
(update network :full-name #(str % " " testnet-name)))]
|
||||
(condp #(contains? %1 %2) (:chain-id network)
|
||||
constants/sepolia-chain-ids (add-testnet-name constants/sepolia-full-name)
|
||||
constants/goerli-chain-ids (add-testnet-name constants/goerli-full-name)
|
||||
network)))
|
||||
|
||||
(defn event-should-be-handled?
|
||||
[db {:keys [topic]}]
|
||||
(some #(= topic %)
|
||||
|
|
|
@ -83,22 +83,33 @@
|
|||
(promesa/then on-success)
|
||||
(promesa/catch on-error)))))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.wallet-connect/prepare-transaction
|
||||
(fn [{:keys [tx chain-id on-success on-error]}]
|
||||
(-> (transactions/prepare-transaction tx
|
||||
chain-id
|
||||
transactions/default-tx-priority)
|
||||
(promesa/then on-success)
|
||||
(promesa/catch on-error))))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.wallet-connect/sign-transaction
|
||||
(fn [{:keys [password address chain-id tx on-success on-error]}]
|
||||
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
|
||||
(-> (transactions/sign-transaction (security/safe-unmask-data password)
|
||||
address
|
||||
tx
|
||||
tx-hash
|
||||
tx-args
|
||||
chain-id)
|
||||
(promesa/then on-success)
|
||||
(promesa/catch on-error))))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.wallet-connect/send-transaction
|
||||
(fn [{:keys [password address chain-id tx on-success on-error]}]
|
||||
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
|
||||
(-> (transactions/send-transaction (security/safe-unmask-data password)
|
||||
address
|
||||
tx
|
||||
tx-hash
|
||||
tx-args
|
||||
chain-id)
|
||||
(promesa/then on-success)
|
||||
(promesa/catch on-error))))
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
networks)
|
||||
required-networks-supported? (wallet-connect-core/required-networks-supported? proposal
|
||||
networks)]
|
||||
(if required-networks-supported?
|
||||
(if (and (not-empty session-networks) required-networks-supported?)
|
||||
{:db (update db
|
||||
:wallet-connect/current-proposal assoc
|
||||
:request proposal
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
(ns status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view
|
||||
(:require [quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.theme]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.style :as style]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn- fees-subtitle
|
||||
[{:keys [text error?]}]
|
||||
(let [theme (quo.theme/use-theme)]
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:color (if error?
|
||||
(colors/resolve-color :danger theme)
|
||||
(colors/theme-colors colors/neutral-100
|
||||
colors/white
|
||||
theme))}}
|
||||
text]))
|
||||
|
||||
(defn view
|
||||
[{:keys [fees fees-error]}]
|
||||
[quo/data-item
|
||||
{:size :small
|
||||
:status :default
|
||||
:card? false
|
||||
:container-style style/data-item
|
||||
:title (i18n/label :t/max-fees)
|
||||
:custom-subtitle (fn [] [fees-subtitle
|
||||
{:text (or fees (i18n/label :t/no-fees))
|
||||
:error? (= fees-error :not-enough-assets-to-pay-gas-fees)}])}])
|
|
@ -13,7 +13,7 @@
|
|||
(rf/dispatch [:wallet-connect/respond-current-session password]))
|
||||
|
||||
(defn view
|
||||
[{:keys [warning-label slide-button-text disabed?]} & children]
|
||||
[{:keys [warning-label slide-button-text disabled?]} & children]
|
||||
(let [{:keys [customization-color]} (rf/sub [:wallet-connect/current-request-account-details])]
|
||||
[rn/view {:style style/content-container}
|
||||
(into [rn/view
|
||||
|
@ -23,7 +23,7 @@
|
|||
[standard-authentication/slide-button
|
||||
{:size :size-48
|
||||
:track-text slide-button-text
|
||||
:disabled? disabed?
|
||||
:disabled? disabled?
|
||||
:customization-color customization-color
|
||||
:on-auth-success on-auth-success
|
||||
:auth-button-label (i18n/label :t/confirm)}]]
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
(ns status-im.contexts.wallet.wallet-connect.modals.send-transaction.view
|
||||
(:require [quo.core :as quo]
|
||||
[quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.data-block.view :as data-block]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view :as
|
||||
fees-data-item]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.footer.view :as footer]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.header.view :as header]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.page-nav.view :as page-nav]
|
||||
|
@ -40,7 +43,7 @@
|
|||
:not-enough-assets
|
||||
:t/not-enough-assets))}])
|
||||
[footer/view
|
||||
{:warning-label (i18n/label :t/wallet-connect-send-transaction-warning)
|
||||
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
|
||||
:slide-button-text (i18n/label :t/slide-to-send)
|
||||
:disabled? error-state}
|
||||
[quo/data-item
|
||||
|
@ -51,11 +54,7 @@
|
|||
:subtitle-type :network
|
||||
:network-image (:source network)
|
||||
:subtitle (:full-name network)}]
|
||||
[quo/data-item
|
||||
{:size :small
|
||||
:status :default
|
||||
:card? false
|
||||
:container-style style/data-item
|
||||
:title (i18n/label :t/max-fees)
|
||||
:subtitle (or max-fees-fiat-formatted (i18n/label :t/no-fees))}]]]]))
|
||||
[fees-data-item/view
|
||||
{:fees max-fees-fiat-formatted
|
||||
:fees-error error-state}]]]]))
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.data-block.view :as data-block]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view :as
|
||||
fees-data-item]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.footer.view :as footer]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.header.view :as header]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.page-nav.view :as page-nav]
|
||||
|
@ -28,13 +30,7 @@
|
|||
:account account}]
|
||||
[data-block/view]]
|
||||
[footer/view
|
||||
{:warning-label (i18n/label :t/wallet-connect-sign-message-warning)
|
||||
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
|
||||
:slide-button-text (i18n/label :t/slide-to-sign)}
|
||||
[quo/data-item
|
||||
{:size :small
|
||||
:status :default
|
||||
:card? false
|
||||
:container-style style/data-item
|
||||
:title (i18n/label :t/max-fees)
|
||||
:subtitle (i18n/label :t/no-fees)}]]]]))
|
||||
[fees-data-item/view]]]]))
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.data-block.view :as data-block]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.fees-data-item.view :as
|
||||
fees-data-item]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.footer.view :as footer]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.header.view :as header]
|
||||
[status-im.contexts.wallet.wallet-connect.modals.common.page-nav.view :as page-nav]
|
||||
|
@ -40,7 +42,7 @@
|
|||
:not-enough-assets
|
||||
:t/not-enough-assets))}])
|
||||
[footer/view
|
||||
{:warning-label (i18n/label :t/wallet-connect-sign-transaction-warning)
|
||||
{:warning-label (i18n/label :t/wallet-connect-sign-warning)
|
||||
:slide-button-text (i18n/label :t/slide-to-sign)
|
||||
:disabled? error-state}
|
||||
[quo/data-item
|
||||
|
@ -51,11 +53,7 @@
|
|||
:subtitle-type :network
|
||||
:network-image (:source network)
|
||||
:subtitle (:full-name network)}]
|
||||
[quo/data-item
|
||||
{:size :small
|
||||
:status :default
|
||||
:card? false
|
||||
:container-style style/data-item
|
||||
:title (i18n/label :t/max-fees)
|
||||
:subtitle (or max-fees-fiat-formatted (i18n/label :t/no-fees))}]]]]))
|
||||
[fees-data-item/view
|
||||
{:fees max-fees-fiat-formatted
|
||||
:fees-error error-state}]]]]))
|
||||
|
||||
|
|
|
@ -1,18 +1,32 @@
|
|||
(ns status-im.contexts.wallet.wallet-connect.processing-events
|
||||
(:require [clojure.string :as string]
|
||||
(:require [cljs-bean.core :as bean]
|
||||
[clojure.string :as string]
|
||||
[native-module.core :as native-module]
|
||||
[re-frame.core :as rf]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
|
||||
[status-im.contexts.wallet.wallet-connect.transactions :as transactions]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/show-request-modal
|
||||
(fn [{:keys [db]}]
|
||||
(let [event (get-in db [:wallet-connect/current-request :event])
|
||||
method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)]
|
||||
(if screen
|
||||
{:fx [[:dispatch [:open-modal screen]]]}
|
||||
(log/error "Didn't find screen for Wallet Connect method"
|
||||
{:method method
|
||||
:event :wallet-connect/process-session-request})))))
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/process-session-request
|
||||
(fn [{:keys [db]} [event]]
|
||||
(let [method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)]
|
||||
(if screen
|
||||
existing-event (get-in db [:wallet-connect/current-request :event])]
|
||||
;; NOTE: make sure we don't show two requests at the same time
|
||||
(when-not existing-event
|
||||
{:db (assoc-in db [:wallet-connect/current-request :event] event)
|
||||
:fx [(condp = method
|
||||
constants/wallet-connect-eth-send-transaction-method
|
||||
|
@ -31,12 +45,7 @@
|
|||
[:dispatch [:wallet-connect/process-sign-typed]]
|
||||
|
||||
constants/wallet-connect-personal-sign-method
|
||||
[:dispatch [:wallet-connect/process-personal-sign]])
|
||||
|
||||
[:dispatch [:open-modal screen]]]}
|
||||
(log/error "Didn't find screen for Wallet Connect method"
|
||||
{:method method
|
||||
:event :wallet-connect/process-session-request})))))
|
||||
[:dispatch [:wallet-connect/process-personal-sign]])]}))))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/process-personal-sign
|
||||
|
@ -48,7 +57,8 @@
|
|||
assoc
|
||||
:address (string/lower-case address)
|
||||
:raw-data raw-data
|
||||
:display-data (or parsed-data raw-data))})))
|
||||
:display-data (or parsed-data raw-data))
|
||||
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/process-eth-sign
|
||||
|
@ -60,44 +70,53 @@
|
|||
assoc
|
||||
:address (string/lower-case address)
|
||||
:raw-data raw-data
|
||||
:display-data (or parsed-data raw-data))})))
|
||||
:display-data (or parsed-data raw-data))
|
||||
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/prepare-transaction-success
|
||||
(fn [{:keys [db]} [prepared-tx chain-id]]
|
||||
(let [{:keys [tx-args]} prepared-tx
|
||||
tx (bean/->clj tx-args)
|
||||
address (-> tx :from string/lower-case)
|
||||
display-data (transactions/beautify-transaction tx)]
|
||||
{:db (update-in db
|
||||
[:wallet-connect/current-request]
|
||||
assoc
|
||||
:address address
|
||||
:raw-data prepared-tx
|
||||
:transaction tx
|
||||
:chain-id chain-id
|
||||
:display-data display-data)
|
||||
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/process-eth-send-transaction
|
||||
(fn [{:keys [db]}]
|
||||
(let [event (wallet-connect-core/get-db-current-request-event db)
|
||||
display-data (-> event
|
||||
clj->js
|
||||
(js/JSON.stringify nil 2))
|
||||
|
||||
{:keys [from] :as tx} (-> event wallet-connect-core/get-request-params first)
|
||||
tx (-> event wallet-connect-core/get-request-params first)
|
||||
chain-id (-> event
|
||||
(get-in [:params :chainId])
|
||||
wallet-connect-core/eip155->chain-id)]
|
||||
{:db (update-in db
|
||||
[:wallet-connect/current-request]
|
||||
assoc
|
||||
:address (string/lower-case from)
|
||||
:raw-data tx
|
||||
{:fx [[:effects.wallet-connect/prepare-transaction
|
||||
{:tx tx
|
||||
:chain-id chain-id
|
||||
:display-data display-data)})))
|
||||
:on-success #(rf/dispatch [:wallet-connect/prepare-transaction-success % chain-id])
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/process-eth-sign-transaction
|
||||
(fn [{:keys [db]}]
|
||||
(let [event (wallet-connect-core/get-db-current-request-event db)
|
||||
display-data (.stringify js/JSON (clj->js event) nil 2)
|
||||
{:keys [from] :as tx} (-> event wallet-connect-core/get-request-params first)
|
||||
tx (-> event wallet-connect-core/get-request-params first)
|
||||
chain-id (-> event
|
||||
(get-in [:params :chainId])
|
||||
wallet-connect-core/eip155->chain-id)]
|
||||
{:db (update-in db
|
||||
[:wallet-connect/current-request]
|
||||
assoc
|
||||
:address (string/lower-case from)
|
||||
:raw-data tx
|
||||
{:fx [[:effects.wallet-connect/prepare-transaction
|
||||
{:tx tx
|
||||
:chain-id chain-id
|
||||
:display-data display-data)})))
|
||||
:on-success #(rf/dispatch [:wallet-connect/prepare-transaction-success % chain-id])
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/process-sign-typed
|
||||
|
@ -108,11 +127,31 @@
|
|||
(transforms/js-dissoc :types :primaryType)
|
||||
(transforms/js-stringify 2))
|
||||
(catch js/Error _ nil))]
|
||||
;; TODO: decide if we should proceed if the typed-data is invalid JSON or fail ahead of time
|
||||
(when (nil? parsed-data) (log/error "Invalid typed data"))
|
||||
(if (nil? parsed-data)
|
||||
{:fx [[:dispatch
|
||||
[:wallet-connect/on-processing-error
|
||||
(ex-info "Failed to parse JSON typed data" {:data raw-data})]]]}
|
||||
{:db (update-in db
|
||||
[:wallet-connect/current-request]
|
||||
assoc
|
||||
:address (string/lower-case address)
|
||||
:display-data (or parsed-data raw-data)
|
||||
:raw-data raw-data)})))
|
||||
:raw-data raw-data)
|
||||
:fx [[:dispatch [:wallet-connect/show-request-modal]]]}))))
|
||||
|
||||
;; TODO: we should reject a request if processing fails
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/on-processing-error
|
||||
(fn [{:keys [db]} [error]]
|
||||
(let [{:keys [address event]} (get db :wallet-connect/current-request)
|
||||
method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)]
|
||||
(log/error "Failed to process Wallet Connect request"
|
||||
{:error error
|
||||
:address address
|
||||
:method method
|
||||
:wallet-connect-event event
|
||||
:event :wallet-connect/on-processing-error})
|
||||
|
||||
{:fx [[:dispatch [:dismiss-modal screen]]
|
||||
[:dispatch [:wallet-connect/reset-current-request]]]})))
|
||||
|
|
|
@ -58,32 +58,36 @@
|
|||
(rf/reg-event-fx
|
||||
:wallet-connect/respond-send-transaction-data
|
||||
(fn [{:keys [db]} [password]]
|
||||
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)]
|
||||
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
|
||||
{:keys [tx-hash tx-args]} raw-data]
|
||||
{:fx [[:effects.wallet-connect/send-transaction
|
||||
{:password password
|
||||
:address address
|
||||
:chain-id chain-id
|
||||
:tx raw-data
|
||||
:tx-hash tx-hash
|
||||
:tx-args tx-args
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
|
||||
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/respond-sign-transaction-data
|
||||
(fn [{:keys [db]} [password]]
|
||||
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)]
|
||||
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
|
||||
{:keys [tx-hash tx-args]} raw-data]
|
||||
{:fx [[:effects.wallet-connect/sign-transaction
|
||||
{:password password
|
||||
:address address
|
||||
:chain-id chain-id
|
||||
:tx raw-data
|
||||
:tx-hash tx-hash
|
||||
:tx-params tx-args
|
||||
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
|
||||
:on-success #(rf/dispatch [:wallet-connect/send-response {:result %}])}]]})))
|
||||
|
||||
;; TODO: should reject if "signing" fails
|
||||
(rf/reg-event-fx
|
||||
:wallet-connect/on-sign-error
|
||||
(fn [{:keys [db]} [error]]
|
||||
(let [event (get-in db [:wallet-connect/current-request :event])
|
||||
{:keys [raw-data address]} (get db :wallet-connect/current-request)
|
||||
(let [{:keys [raw-data address event]} (get db :wallet-connect/current-request)
|
||||
method (wallet-connect-core/get-request-method event)
|
||||
screen (wallet-connect-core/method-to-screen method)]
|
||||
(log/error "Failed to sign Wallet Connect request"
|
||||
|
|
|
@ -59,3 +59,8 @@
|
|||
password
|
||||
chain-id
|
||||
legacy?))
|
||||
|
||||
(defn wallet-get-suggested-fees
|
||||
[chain-id]
|
||||
(-> (call-rpc "wallet_getSuggestedFees" chain-id)
|
||||
(promesa/then transforms/js->clj)))
|
||||
|
|
|
@ -1,10 +1,24 @@
|
|||
(ns status-im.contexts.wallet.wallet-connect.transactions
|
||||
(:require [cljs-bean.core :as bean]
|
||||
[clojure.string :as string]
|
||||
[native-module.core :as native-module]
|
||||
[promesa.core :as promesa]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as core]
|
||||
[status-im.contexts.wallet.wallet-connect.rpc :as rpc]
|
||||
[utils.money :as money]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(defn transaction-request?
|
||||
[event]
|
||||
(->> (core/get-request-method event)
|
||||
(contains? #{constants/wallet-connect-eth-send-transaction-method
|
||||
constants/wallet-connect-eth-sign-transaction-method})))
|
||||
|
||||
;; NOTE: Currently we don't allow the user to configure the tx priority as we don't
|
||||
;; show the estimated time, but when we implement it, we should allow to change it
|
||||
(def ^:constant default-tx-priority :medium)
|
||||
|
||||
(defn- strip-hex-prefix
|
||||
"Strips the extra 0 in hex value if present"
|
||||
[hex-value]
|
||||
|
@ -15,12 +29,12 @@
|
|||
|
||||
(defn- format-tx-hex-values
|
||||
"Due to how status-go expects hex values, we should remove the extra 0s in transaction hex values e.g. 0x0f -> 0xf"
|
||||
[tx]
|
||||
[tx f]
|
||||
(let [tx-keys [:gasLimit :gas :gasPrice :nonce :value :maxFeePerGas :maxPriorityFeePerGas]]
|
||||
(reduce (fn [acc tx-key]
|
||||
(if (and (contains? tx tx-key)
|
||||
(not (nil? (get tx tx-key))))
|
||||
(update acc tx-key strip-hex-prefix)
|
||||
(update acc tx-key f)
|
||||
acc))
|
||||
tx
|
||||
tx-keys)))
|
||||
|
@ -29,25 +43,99 @@
|
|||
"Formats the transaction and transforms it into a stringified JS object, ready to be passed to an RPC call."
|
||||
[tx]
|
||||
(-> tx
|
||||
format-tx-hex-values
|
||||
;; NOTE: removing `:nonce` to compute it when building the transaction on status-go
|
||||
(dissoc :nonce)
|
||||
(format-tx-hex-values strip-hex-prefix)
|
||||
bean/->js
|
||||
(transforms/js-stringify 0)))
|
||||
|
||||
(defn beautify-transaction
|
||||
[tx]
|
||||
(let [hex->number #(-> % (subs 2) native-module/hex-to-number)]
|
||||
(-> tx
|
||||
(format-tx-hex-values hex->number)
|
||||
clj->js
|
||||
(js/JSON.stringify nil 2))))
|
||||
|
||||
(defn- gwei->hex
|
||||
[gwei]
|
||||
(->> gwei
|
||||
money/gwei->wei
|
||||
native-module/number-to-hex
|
||||
(str "0x")))
|
||||
|
||||
(defn- get-max-fee-per-gas-key
|
||||
"Mapping transaction priority (which determines how quickly a tx is processed)
|
||||
to the `suggested-routes` key that should be used for `:maxPriorityFeePerGas`.
|
||||
|
||||
Returns `:high` | `:medium` | `:low`"
|
||||
[tx-priority]
|
||||
(get {:high :maxFeePerGasHigh
|
||||
:medium :maxFeePerGasMedium
|
||||
:low :maxFeePerGasLow}
|
||||
tx-priority))
|
||||
|
||||
(defn- dynamic-fee-tx?
|
||||
"Checks if a transaction has dynamic fees (EIP1559)"
|
||||
[tx]
|
||||
(every? tx [:maxFeePerGas :maxPriorityFeePerGas]))
|
||||
|
||||
(defn- tx->eip1559-tx
|
||||
"Adds `:maxFeePerGas` and `:maxPriorityFeePerGas` for dynamic fee support (EIP1559) and
|
||||
removes `:gasPrice`, if the chain supports EIP1559 and the transaction doesn't already
|
||||
have dynamic fees."
|
||||
[tx suggested-fees tx-priority]
|
||||
(if (and (:eip1559Enabled suggested-fees)
|
||||
(not (dynamic-fee-tx? tx)))
|
||||
(let [max-fee-per-gas-key (get-max-fee-per-gas-key tx-priority)
|
||||
max-fee-per-gas (-> suggested-fees max-fee-per-gas-key gwei->hex)
|
||||
max-priority-fee-per-gas (-> suggested-fees :maxPriorityFeePerGas gwei->hex)]
|
||||
(-> tx
|
||||
(assoc
|
||||
:maxFeePerGas max-fee-per-gas
|
||||
:maxPriorityFeePerGas max-priority-fee-per-gas)
|
||||
;; NOTE: `:gasPrice` is used only for legacy Tx, so we discard it in favor of dynamic fees
|
||||
(dissoc :gasPrice)))
|
||||
tx))
|
||||
|
||||
(defn- prepare-transaction-fees
|
||||
"Makes sure the transaction has the correct gas and fees properties"
|
||||
[tx tx-priority suggested-fees]
|
||||
(-> (assoc tx
|
||||
;; NOTE: `gasLimit` is ignored on status-go when building a transaction
|
||||
;; (`wallet_buildTransaction`), so we're setting it as the `gas` property
|
||||
:gas
|
||||
(or (:gasLimit tx)
|
||||
(:gas tx)))
|
||||
(dissoc :gasLimit)
|
||||
(tx->eip1559-tx suggested-fees tx-priority)))
|
||||
|
||||
(defn prepare-transaction
|
||||
"Formats and builds the incoming transaction, adding the missing properties and returning the final
|
||||
transaction, along with the transaction hash and the suggested fees"
|
||||
[tx chain-id tx-priority]
|
||||
(promesa/let [suggested-fees (rpc/wallet-get-suggested-fees chain-id)
|
||||
{:keys [tx-args message-to-sign]} (->>
|
||||
(prepare-transaction-fees tx
|
||||
tx-priority
|
||||
suggested-fees)
|
||||
prepare-transaction-for-rpc
|
||||
(rpc/wallet-build-transaction chain-id))]
|
||||
{:tx-args tx-args
|
||||
:tx-hash message-to-sign
|
||||
:suggested-fees suggested-fees}))
|
||||
|
||||
(defn sign-transaction
|
||||
[password address tx chain-id]
|
||||
[password address tx-hash tx-args chain-id]
|
||||
(promesa/let
|
||||
[formatted-tx (prepare-transaction-for-rpc tx)
|
||||
{:keys [message-to-sign tx-args]} (rpc/wallet-build-transaction chain-id formatted-tx)
|
||||
signature (rpc/wallet-sign-message message-to-sign address password)
|
||||
[signature (rpc/wallet-sign-message tx-hash address password)
|
||||
raw-tx (rpc/wallet-build-raw-transaction chain-id tx-args signature)]
|
||||
raw-tx))
|
||||
|
||||
(defn send-transaction
|
||||
[password address tx chain-id]
|
||||
[password address tx-hash tx-args chain-id]
|
||||
(promesa/let
|
||||
[formatted-tx (prepare-transaction-for-rpc tx)
|
||||
{:keys [message-to-sign tx-args]} (rpc/wallet-build-transaction chain-id formatted-tx)
|
||||
signature (rpc/wallet-sign-message message-to-sign address password)
|
||||
[signature (rpc/wallet-sign-message tx-hash address password)
|
||||
tx (rpc/wallet-send-transaction-with-signature chain-id
|
||||
tx-args
|
||||
signature)]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
[status-im.contexts.wallet.common.utils :as wallet-utils]
|
||||
[status-im.contexts.wallet.common.utils.networks :as networks]
|
||||
[status-im.contexts.wallet.wallet-connect.core :as wallet-connect-core]
|
||||
[status-im.contexts.wallet.wallet-connect.transactions :as transactions]
|
||||
[utils.money :as money]))
|
||||
|
||||
(rf/reg-sub
|
||||
|
@ -57,66 +58,95 @@
|
|||
sessions)))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/current-request-network
|
||||
:wallet-connect/chain-id
|
||||
:<- [:wallet-connect/current-request]
|
||||
(fn [request]
|
||||
(-> request
|
||||
(get-in [:event :params :chainId])
|
||||
(wallet-connect-core/eip155->chain-id)
|
||||
(networks/get-network-details))))
|
||||
(wallet-connect-core/eip155->chain-id))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/current-request-network
|
||||
:<- [:wallet-connect/chain-id]
|
||||
(fn [chain-id]
|
||||
(-> chain-id
|
||||
(networks/get-network-details)
|
||||
(wallet-connect-core/add-full-testnet-name))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/transaction-args
|
||||
:<- [:wallet-connect/current-request]
|
||||
(fn [{:keys [event transaction]}]
|
||||
(when (transactions/transaction-request? event)
|
||||
transaction)))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/transaction-suggested-fees
|
||||
:<- [:wallet-connect/current-request]
|
||||
(fn [{:keys [event raw-data]}]
|
||||
(when (transactions/transaction-request? event)
|
||||
(:suggested-fees raw-data))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/transaction-max-fees-wei
|
||||
:<- [:wallet-connect/transaction-args]
|
||||
:<- [:wallet-connect/transaction-suggested-fees]
|
||||
(fn [[transaction suggested-fees]]
|
||||
(when transaction
|
||||
(let [{:keys [gasPrice gas gasLimit maxFeePerGas]} transaction
|
||||
eip-1559-chain? (:eip1559Enabled suggested-fees)
|
||||
gas-limit (or gasLimit gas)
|
||||
max-gas-fee (if eip-1559-chain? maxFeePerGas gasPrice)]
|
||||
(money/bignumber (* max-gas-fee gas-limit))))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/account-eth-token
|
||||
:<- [:wallet-connect/current-request-address]
|
||||
:<- [:wallet/accounts]
|
||||
(fn [[address accounts]]
|
||||
(let [fee-token "ETH"
|
||||
find-account #(when (= (:address %) address) %)
|
||||
find-token #(when (= (:symbol %) fee-token) %)]
|
||||
(->> accounts
|
||||
(some find-account)
|
||||
:tokens
|
||||
(some find-token)))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/current-request-transaction-information
|
||||
:<- [:wallet-connect/current-request]
|
||||
:<- [:wallet/accounts]
|
||||
:<- [:wallet-connect/chain-id]
|
||||
:<- [:wallet-connect/transaction-max-fees-wei]
|
||||
:<- [:wallet-connect/transaction-args]
|
||||
:<- [:wallet-connect/account-eth-token]
|
||||
:<- [:profile/currency]
|
||||
:<- [:profile/currency-symbol]
|
||||
(fn [[request accounts currency currency-symbol]]
|
||||
(let [chain-id (-> request
|
||||
(get-in [:raw-data :params :chainId])
|
||||
(wallet-connect-core/eip155->chain-id))
|
||||
all-tokens (->> accounts
|
||||
(filter #(= (:address %)
|
||||
(:address request)))
|
||||
(first)
|
||||
:tokens)
|
||||
eth-token (->> all-tokens
|
||||
(filter #(= (:symbol %) "ETH"))
|
||||
(first))
|
||||
{:keys [gasPrice gasLimit value]} (-> request
|
||||
:raw-data
|
||||
wallet-connect-core/get-request-params
|
||||
first)
|
||||
max-fees-wei (money/bignumber (* gasPrice gasLimit))]
|
||||
(when (and gasPrice gasLimit)
|
||||
(fn [[chain-id max-fees-wei transaction eth-token currency currency-symbol]]
|
||||
(when transaction
|
||||
(let [max-fees-ether (money/wei->ether max-fees-wei)
|
||||
token-fiat-value (wallet-utils/calculate-token-fiat-value {:currency currency
|
||||
max-fees-fiat (wallet-utils/calculate-token-fiat-value {:currency currency
|
||||
:balance max-fees-ether
|
||||
:token eth-token})
|
||||
crypto-formatted (wallet-utils/get-standard-crypto-format eth-token max-fees-ether)
|
||||
fiat-formatted (wallet-utils/get-standard-fiat-format crypto-formatted
|
||||
currency-symbol
|
||||
token-fiat-value)
|
||||
max-fees-fiat-formatted (-> max-fees-ether
|
||||
(wallet-utils/get-standard-crypto-format eth-token)
|
||||
(wallet-utils/get-standard-fiat-format currency-symbol
|
||||
max-fees-fiat))
|
||||
balance (-> eth-token
|
||||
(get-in [:balances-per-chain chain-id :raw-balance])
|
||||
(money/bignumber))
|
||||
value (money/bignumber value)
|
||||
total-transaction-value (money/add max-fees-wei value)]
|
||||
money/bignumber)
|
||||
tx-value (money/bignumber (:value transaction))
|
||||
total-transaction-value (money/add max-fees-wei tx-value)]
|
||||
{:total-transaction-value total-transaction-value
|
||||
:balance balance
|
||||
:max-fees max-fees-wei
|
||||
:max-fees-fiat-value token-fiat-value
|
||||
:max-fees-fiat-formatted fiat-formatted
|
||||
:max-fees-fiat-value max-fees-fiat
|
||||
:max-fees-fiat-formatted max-fees-fiat-formatted
|
||||
:error-state (cond
|
||||
(and
|
||||
(money/sufficient-funds? value balance)
|
||||
(not (money/sufficient-funds? total-transaction-value balance)))
|
||||
:not-enough-assets-to-pay-gas-fees
|
||||
|
||||
(not (money/sufficient-funds? value balance))
|
||||
(not (money/sufficient-funds? tx-value balance))
|
||||
:not-enough-assets
|
||||
|
||||
:else nil)})))))
|
||||
(not (money/sufficient-funds? total-transaction-value
|
||||
balance))
|
||||
:not-enough-assets-to-pay-gas-fees)}))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet-connect/current-proposal-request
|
||||
|
|
|
@ -148,6 +148,10 @@
|
|||
[n]
|
||||
(wei-> :gwei n))
|
||||
|
||||
(defn gwei->wei
|
||||
[n]
|
||||
(->wei :gwei n))
|
||||
|
||||
(defn ether->wei
|
||||
[^js bn]
|
||||
(when bn
|
||||
|
|
|
@ -1941,8 +1941,8 @@
|
|||
"pinned-by": "Pinned by",
|
||||
"pin-limit-reached": "Pin limit reached. Unpin a previous message first.",
|
||||
"no-fees": "No fees",
|
||||
"not-enough-assets-to-pay-gas-fees": "Not enough assets to pay gas fees.",
|
||||
"not-enough-assets": "Not enough assets to complete transaction.",
|
||||
"not-enough-assets-to-pay-gas-fees": "Not enough assets to pay gas fees",
|
||||
"not-enough-assets": "Not enough assets to complete transaction",
|
||||
"max-fee": "Max fee",
|
||||
"max-fees": "Max fees",
|
||||
"max-priority-fee": "Max priority fee",
|
||||
|
@ -2052,6 +2052,7 @@
|
|||
"wallet-connect-sign-message-header": "wants you to sign the message with",
|
||||
"wallet-connect-send-transaction-header": "wants you to send this transaction with",
|
||||
"wallet-connect-sign-transaction-header": "wants you to sign this transaction with",
|
||||
"wallet-connect-sign-warning": "Sign only if you trust the dApp",
|
||||
"wallet-connect-sign-message-warning": "Sign messages only if you trust the dApp",
|
||||
"wallet-connect-send-transaction-warning": "Send transactions only if you trust the dApp",
|
||||
"wallet-connect-sign-transaction-warning": "Sign transactions only if you trust the dApp",
|
||||
|
@ -2746,7 +2747,6 @@
|
|||
"saved-address-network-preference-selection-description": "Only change if you know which networks the address owner is happy to to receive funds on",
|
||||
"add-preferences": "Add preferences",
|
||||
"buy-eth": "Buy ETH",
|
||||
"not-enough-assets": "Not enough assets to pay gas fees",
|
||||
"send-from-network": "Send from {{network}}",
|
||||
"define-amount-sent-from-network": "Define amount sent from {{network}} network",
|
||||
"dont-auto-recalculate-network": "Don't auto recalculate {{network}}",
|
||||
|
|
Loading…
Reference in New Issue