diff --git a/.env b/.env index 7cd6a8de7e..230bbac8fb 100644 --- a/.env +++ b/.env @@ -29,4 +29,4 @@ APN_TOPIC=im.status.ethereum.pr COMMUNITIES_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1 METRICS_ENABLED=0 -EIP1559_ENABLED=0 +EIP1559_ENABLED=1 diff --git a/.env.e2e b/.env.e2e index 380294e221..a97aa85857 100644 --- a/.env.e2e +++ b/.env.e2e @@ -30,3 +30,4 @@ DATABASE_MANAGEMENT_ENABLED=1 COMMUNITIES_ENABLED=1 COMMUNITIES_MANAGEMENT_ENABLED=1 METRICS_ENABLED=0 +EIP1559_ENABLED=1 diff --git a/.env.jenkins b/.env.jenkins index 55e385bcf8..90ac5585b3 100644 --- a/.env.jenkins +++ b/.env.jenkins @@ -30,3 +30,4 @@ DATABASE_MANAGEMENT_ENABLED=1 COMMUNITIES_ENABLED=1 COMMUNITIES_MANAGEMENT_ENABLED=1 METRICS_ENABLED=0 +EIP1559_ENABLED=1 diff --git a/.env.nightly b/.env.nightly index 87c17f3a6b..cd12f14abd 100644 --- a/.env.nightly +++ b/.env.nightly @@ -23,3 +23,4 @@ BLANK_PREVIEW=0 COMMUNITIES_ENABLED=1 DATABASE_MANAGEMENT_ENABLED=1 METRICS_ENABLED=0 +EIP1559_ENABLED=1 diff --git a/.env.release b/.env.release index 213838ce59..e5666cfadc 100644 --- a/.env.release +++ b/.env.release @@ -19,3 +19,4 @@ ENABLE_ROOT_ALERT=1 MAX_IMAGES_BATCH=1 ENABLE_REFERRAL_INVITE=0 METRICS_ENABLED=0 +EIP1559_ENABLED=1 diff --git a/src/status_im/browser/core.cljs b/src/status_im/browser/core.cljs index 51a06f6165..38b7438a5a 100644 --- a/src/status_im/browser/core.cljs +++ b/src/status_im/browser/core.cljs @@ -406,7 +406,10 @@ :typed? typed? :pinless? (= method constants/web3-keycard-sign-typed-data) :from dapps-address}} - {:tx-obj (update (first params) :from #(or % dapps-address))}) + {:tx-obj (-> params + first + (update :from #(or % dapps-address)) + (dissoc :gasPrice))}) {:on-result [:browser.dapp/transaction-on-result message-id id] :on-error [:browser.dapp/transaction-on-error message-id]})))) (if (#{"eth_accounts" "eth_coinbase"} method) diff --git a/src/status_im/ethereum/json_rpc.cljs b/src/status_im/ethereum/json_rpc.cljs index f3f6ec0833..e35c144d11 100644 --- a/src/status_im/ethereum/json_rpc.cljs +++ b/src/status_im/ethereum/json_rpc.cljs @@ -28,6 +28,7 @@ "eth_newFilter" {:subscription? true} "eth_getCode" {} "eth_syncing" {} + "eth_feeHistory" {} "net_version" {} "web3_clientVersion" {} "shh_generateSymKeyFromPassword" {} diff --git a/src/status_im/ethereum/transactions/core.cljs b/src/status_im/ethereum/transactions/core.cljs index d1efb32fc1..760a7021ab 100644 --- a/src/status_im/ethereum/transactions/core.cljs +++ b/src/status_im/ethereum/transactions/core.cljs @@ -50,7 +50,8 @@ (defn enrich-transfer [chain-tokens {:keys [address blockNumber timestamp from txStatus txHash gasPrice - gasUsed contract value gasLimit input nonce to type id]}] + gasUsed contract value gasLimit input nonce to type id + maxFeePerGas maxPriorityFeePerGas]}] (let [erc20? (= type "erc20") failed? (= txStatus "0x0")] (merge {:address (eip55/address->checksum address) @@ -59,6 +60,8 @@ :timestamp (* (decode/uint timestamp) 1000) :gas-used (str (decode/uint gasUsed)) :gas-price (str (decode/uint gasPrice)) + :fee-cap (str (decode/uint maxFeePerGas)) + :tip-cap (str (decode/uint maxPriorityFeePerGas)) :gas-limit (str (decode/uint gasLimit)) :nonce (str (decode/uint nonce)) :hash txHash diff --git a/src/status_im/signing/core.cljs b/src/status_im/signing/core.cljs index 9db120d550..05c91394eb 100644 --- a/src/status_im/signing/core.cljs +++ b/src/status_im/signing/core.cljs @@ -103,9 +103,11 @@ (when gasPrice {:gasPrice (str "0x" (abi-spec/number-to-hex gasPrice))}) (when maxPriorityFeePerGas - {:maxPriorityFeePerGas (str "0x" (abi-spec/number-to-hex (.decimalPlaces maxPriorityFeePerGas 0)))}) + {:maxPriorityFeePerGas (str "0x" (abi-spec/number-to-hex + (js/parseInt maxPriorityFeePerGas)))}) (when maxFeePerGas - {:maxFeePerGas (str "0x" (abi-spec/number-to-hex (.decimalPlaces maxFeePerGas 0)))}))] + {:maxFeePerGas (str "0x" (abi-spec/number-to-hex + (js/parseInt maxFeePerGas)))}))] (when-not in-progress? {:db (update db :signing/sign assoc :error nil :in-progress? true) :signing/send-transaction-fx {:tx-obj tx-obj-to-send @@ -113,7 +115,8 @@ :cb #(re-frame/dispatch [:signing/transaction-completed % tx-obj-to-send hashed-password])}}))))) (fx/defn prepare-unconfirmed-transaction - [{:keys [db now]} new-tx-hash {:keys [value gasPrice gas data to from hash]} symbol amount] + [{:keys [db now]} new-tx-hash + {:keys [value gasPrice maxFeePerGas maxPriorityFeePerGas gas data to from hash]} symbol amount] (let [token (tokens/symbol->token (:wallet/all-tokens db) symbol) from (eip55/address->checksum from) ;;if there is a hash in the tx object that means we resending transaction @@ -132,6 +135,8 @@ (money/to-fixed (money/unit->token amount (:decimals token))) (money/to-fixed (money/bignumber value))) :gas-price gas-price + :fee-cap maxFeePerGas + :tip-cap maxPriorityFeePerGas :gas-limit gas-limit}] (log/info "[signing] prepare-unconfirmed-transaction" tx) {:db (-> db @@ -246,15 +251,25 @@ (prices/update-prices) #(when-not gas {:db (assoc-in (:db %) [:signing/edit-fee :gas-loading?] true) - :signing/update-estimated-gas {:obj (dissoc tx-obj :gasPrice) + :signing/update-estimated-gas {:obj (-> tx-obj + (dissoc :gasPrice) + (update :maxFeePerGas + (fn [fee] + (some-> fee + money/bignumber + (money/mul 2) + money/to-hex)))) :success-event :signing/update-estimated-gas-success :error-event :signing/update-estimated-gas-error}}) - #(when-not (or maxFeePerGas gasPrice) - {:db (assoc-in (:db %) [:signing/edit-fee :gas-price-loading?] true) - :signing/update-gas-price {:success-event :signing/update-gas-price-success - :error-event :signing/update-gas-price-error - :network-id (get-in (ethereum/current-network db) - [:config :NetworkId])}}))))) + (fn [cofx] + (when-not (or maxFeePerGas gasPrice) + {:db (assoc-in (:db cofx) [:signing/edit-fee :gas-price-loading?] true) + :signing/update-gas-price + {:success-callback #(re-frame/dispatch + [:wallet.send/update-gas-price-success :signing/tx %]) + :error-callback #(re-frame/dispatch [:signing/update-gas-price-error %]) + :network-id (get-in (ethereum/current-network db) + [:config :NetworkId])}})))))) (fx/defn check-queue [{:keys [db] :as cofx}] (let [{:signing/keys [tx queue]} db] @@ -436,7 +451,7 @@ to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) from-address (:address from) identity (:current-chat-id db) - db (dissoc db :wallet/prepare-transaction)] + db (dissoc db :wallet/prepare-transaction :signing/edit-fee)] (if to-norm (fx/merge cofx @@ -474,7 +489,7 @@ to-norm (:address request-parameters) from-address (:address from)] (fx/merge cofx - {:db (dissoc db :wallet/prepare-transaction)} + {:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)} (fn [cofx] (sign cofx @@ -502,7 +517,7 @@ to-norm (ethereum/normalized-hex (if (string? to) to (:address to))) from-address (:address from)] (fx/merge cofx - {:db (dissoc db :wallet/prepare-transaction)} + {:db (dissoc db :wallet/prepare-transaction :signing/edit-fee)} (sign {:tx-obj (merge (if (eip1559/sync-enabled?) {:from from-address diff --git a/src/status_im/signing/eip1559.cljs b/src/status_im/signing/eip1559.cljs index 9923643be7..17a5d7695e 100644 --- a/src/status_im/signing/eip1559.cljs +++ b/src/status_im/signing/eip1559.cljs @@ -34,11 +34,10 @@ :on-error #(callback nil)})) (defn sync-enabled? [] - (and config/eip1559-enabled? - @activated-on-current-network?)) + config/eip1559-enabled?) (defn enabled? [network-id enabled-callback disabled-callback] - (let [london-activated? (get @activated? network-id)] + (let [london-activated? true] (cond (not config/eip1559-enabled?) (disabled-callback) diff --git a/src/status_im/signing/gas.cljs b/src/status_im/signing/gas.cljs index 99b550e38d..5afe01a7ee 100644 --- a/src/status_im/signing/gas.cljs +++ b/src/status_im/signing/gas.cljs @@ -62,17 +62,36 @@ (assoc key data) edit-max-fee))) -(def minimum-priority-fee - (money/wei-> :gwei (money/->wei :gwei 1))) +;; TODO(rasom): this number is almost arbitrary, I was able to sent txs with +;; 0.2 gwei tip, so it should be revisited lately +(def minimum-priority-fee-gwei + (money/bignumber 0.3)) + +(defn get-suggested-tip [latest-priority-fee] + (money/bignumber latest-priority-fee)) + +(defn get-minimum-priority-fee [latest-priority-fee] + (let [latest-priority-fee-bn (money/bignumber latest-priority-fee) + suggested-tip-gwei (money/wei->gwei (get-suggested-tip latest-priority-fee-bn))] + (if (money/greater-than minimum-priority-fee-gwei suggested-tip-gwei) + (money/div suggested-tip-gwei 2) + minimum-priority-fee-gwei))) + +(defn get-suggestions-range [latest-priority-fee] + (let [current-minimum-fee (get-minimum-priority-fee latest-priority-fee)] + [(if (money/greater-than minimum-priority-fee-gwei current-minimum-fee) + current-minimum-fee + minimum-priority-fee-gwei) + (money/wei->gwei (money/bignumber latest-priority-fee))])) (def average-priority-fee - (money/wei-> :gwei (money/->wei :gwei 1.5))) + (money/wei->gwei (money/->wei :gwei 1.5))) (defn validate-max-fee [db] (let [{:keys [maxFeePerGas maxPriorityFeePerGas]} (get db :signing/edit-fee) - latest-base-fee (money/wei-> :gwei - (money/bignumber - (get db :wallet/latest-base-fee))) + latest-base-fee (money/wei->gwei + (money/bignumber + (get db :wallet/current-base-fee))) fee-error (cond (or (:error maxFeePerGas) (:error maxPriorityFeePerGas)) @@ -81,12 +100,6 @@ (money/greater-than latest-base-fee (:value-number maxFeePerGas)) {:label (i18n/label :t/below-base-fee) - :severity :error} - - (money/greater-than (:value-number maxPriorityFeePerGas) - (money/sub (:value-number maxFeePerGas) - latest-base-fee)) - {:label (i18n/label :t/reduced-tip) :severity :error})] (if fee-error (assoc-in db [:signing/edit-fee :maxFeePerGas :fee-error] fee-error) @@ -94,19 +107,22 @@ (defn validate-max-priority-fee [db] (let [{:keys [maxPriorityFeePerGas]} (get db :signing/edit-fee) + latest-priority-fee (get db :wallet/current-priority-fee) fee-error (cond (:error maxPriorityFeePerGas) nil - (money/greater-than minimum-priority-fee + (money/greater-than (get-minimum-priority-fee + (money/div + (money/wei->gwei (money/bignumber latest-priority-fee)) 2)) (:value-number maxPriorityFeePerGas)) {:label (i18n/label :t/low-tip) :severity :error} - (money/greater-than average-priority-fee - (:value-number maxPriorityFeePerGas)) - {:label (i18n/label :t/lower-than-average-tip) - :severity :error})] + #_(money/greater-than average-priority-fee + (:value-number maxPriorityFeePerGas)) + #_{:label (i18n/label :t/lower-than-average-tip) + :severity :error})] (if fee-error (assoc-in db [:signing/edit-fee :maxPriorityFeePerGas :fee-error] fee-error) (update-in db [:signing/edit-fee :maxPriorityFeePerGas] dissoc :fee-error)))) @@ -125,20 +141,54 @@ {:events [:signing.edit-fee.ui/edit-value]} [{:keys [db]} key value] {:db (-> db + (assoc-in [:signing/edit-fee :selected-fee-option] :custom) (update :signing/edit-fee build-edit key value) validate-eip1559-fees)}) +(defn get-fee-options [tip slow normal fast] + (let [tip-bn (money/bignumber tip) + normal-bn (money/bignumber normal) + slow-bn (money/bignumber slow) + fast-bn (money/bignumber fast)] + {:normal + {:base-fee normal-bn + :tip tip-bn + :fee (money/add normal-bn tip-bn)} + :slow + {:base-fee slow-bn + :tip tip-bn + :fee (money/add slow-bn tip-bn)} + :fast + {:base-fee fast-bn + :tip tip-bn + :fee (money/add fast-bn tip-bn)}})) + +(fx/defn set-fee-option + {:events [:signing.edit-fee.ui/set-option]} + [{:keys [db] :as cofx} option] + (let [tip (get db :wallet/current-priority-fee) + slow (get db :wallet/slow-base-fee) + normal (get db :wallet/normal-base-fee) + fast (get db :wallet/fast-base-fee) + {:keys [fee tip]} (get (get-fee-options tip slow normal fast) option)] + {:db (-> db + (assoc-in [:signing/edit-fee :selected-fee-option] option) + (update :signing/edit-fee + build-edit :maxFeePerGas (money/wei->gwei fee)) + (update :signing/edit-fee + build-edit :maxPriorityFeePerGas (money/wei->gwei tip)))})) + (fx/defn set-priority-fee {:events [:signing.edit-fee.ui/set-priority-fee]} [{:keys [db]} value] (let [{:keys [maxFeePerGas maxPriorityFeePerGas]} (get db :signing/edit-fee) - latest-base-fee (get db :wallet/latest-base-fee) + latest-base-fee (get db :wallet/current-base-fee) max-fee-value (:value-number maxFeePerGas) max-priority-fee-value (:value-number maxPriorityFeePerGas) new-value (money/bignumber value) fee-without-tip (money/sub max-fee-value max-priority-fee-value) - base-fee (money/wei-> :gwei (money/bignumber latest-base-fee)) + base-fee (money/wei->gwei (money/bignumber latest-base-fee)) new-max-fee-value (money/to-fixed (if (money/greater-than base-fee fee-without-tip) @@ -159,9 +209,21 @@ (fx/defn update-gas-price-success {:events [:signing/update-gas-price-success]} [{db :db} price] - {:db (-> db - (assoc-in [:signing/tx :gasPrice] price) - (assoc-in [:signing/edit-fee :gas-price-loading?] false))}) + (if (eip1559/sync-enabled?) + (let [{:keys [normal-base-fee max-priority-fee]} price + max-priority-fee-bn (money/with-precision + (get-suggested-tip max-priority-fee) + 0)] + {:db (-> db + (assoc-in [:signing/tx :maxFeePerGas] + (money/to-hex (money/add max-priority-fee-bn + (money/bignumber normal-base-fee)))) + (assoc-in [:signing/tx :maxPriorityFeePerGas] + (money/to-hex max-priority-fee-bn)) + (assoc-in [:signing/edit-fee :gas-price-loading?] false))}) + {:db (-> db + (assoc-in [:signing/tx :gasPrice] price) + (assoc-in [:signing/edit-fee :gas-price-loading?] false))})) (fx/defn update-estimated-gas-error {:events [:signing/update-estimated-gas-error]} @@ -179,12 +241,12 @@ {:events [:signing.ui/open-fee-sheet]} [{{:signing/keys [tx] :as db} :db :as cofx} sheet-opts] (let [{:keys [gas gasPrice maxFeePerGas maxPriorityFeePerGas]} tx - max-fee (money/to-fixed (money/wei-> :gwei maxFeePerGas)) - max-priority-fee (money/to-fixed (money/wei-> :gwei maxPriorityFeePerGas)) + max-fee (money/to-fixed (money/wei->gwei maxFeePerGas)) + max-priority-fee (money/to-fixed (money/wei->gwei maxPriorityFeePerGas)) edit-fee (reduce (partial apply build-edit) - {} + {:selected-fee-option (get-in db [:signing/edit-fee :selected-fee-option])} {:gas (money/to-fixed gas) - :gasPrice (money/to-fixed (money/wei-> :gwei gasPrice)) + :gasPrice (money/to-fixed (money/wei->gwei gasPrice)) :maxFeePerGas max-fee :maxPriorityFeePerGas max-priority-fee})] (fx/merge cofx @@ -214,57 +276,100 @@ (re-frame/reg-fx :signing/update-gas-price - (fn [{:keys [success-event error-event network-id] :as params}] + (fn [{:keys [success-callback error-callback network-id] :as params}] (eip1559/enabled? network-id (fn [] (json-rpc/call - {:method "eth_getBlockByNumber" - :params ["latest" false] + {:method "eth_feeHistory" + :params [101 "latest" nil] :on-success #(re-frame/dispatch [::header-fetched - (assoc params :header %)]) - :on-error #(re-frame/dispatch [error-event %])})) + (assoc params :fee-history %)])})) (fn [] (json-rpc/call {:method "eth_gasPrice" - :on-success #(re-frame/dispatch [success-event %]) - :on-error #(re-frame/dispatch [error-event %])}))))) - -(fx/defn header-fetched - {:events [::header-fetched]} - [_ {:keys [error-event] :as params}] - {::json-rpc/call - [{:method "eth_maxPriorityFeePerGas" - :on-success #(re-frame/dispatch [::max-priority-fee-per-gas-fetched - (assoc params :max-priority-fee %)]) - :on-error (if error-event - #(re-frame/dispatch [error-event %]) - #(log/error "Can't fetch header" %))}]}) + :on-success #(success-callback %) + :on-error #(error-callback %)}))))) (def london-block-gas-limit (money/bignumber 30000000)) -(defn check-base-fee [{:keys [gasUsed baseFeePerGas]}] - {:base-fee baseFeePerGas - :spike? (or (money/greater-than-or-equals - (money/bignumber 0) - (money/bignumber gasUsed)) - (money/greater-than-or-equals - (money/bignumber gasUsed) - (money/bignumber london-block-gas-limit)))}) +(defn calc-percentiles [v ps] + (let [sorted-v (sort-by + identity + (fn [a b] + (status-im.utils.money/greater-than b a)) + v)] + (reduce + (fn [acc p] + (assoc acc p (nth sorted-v p))) + {} + ps))) -(fx/defn max-priority-fee-per-gas-fetched - {:events [::max-priority-fee-per-gas-fetched]} - [_ {:keys [success-event header max-priority-fee]}] - (let [{:keys [base-fee spike?]} (check-base-fee header)] - {:dispatch [success-event {:base-fee base-fee - :max-priority-fee max-priority-fee - :spike? spike?}]})) +;; It is very unlikely to be that small on mainnet, but on testnets current base +;; fee might be very small and using this value might slow transaction +(def minimum-base-fee (money/->wei :gwei (money/bignumber 1))) + +(defn recommended-base-fee [current perc20 perc80] + (let [fee + (cond (and (money/greater-than-or-equals current perc20) + (money/greater-than-or-equals perc80 current)) + current + + (money/greater-than perc20 current) + perc20 + + :else + perc80)] + (if (money/greater-than minimum-base-fee fee) + minimum-base-fee + fee))) + +(defn slow-base-fee [_ perc10] perc10) + +(defn fast-base-fee [current _] + (let [fee (money/mul current 2)] + (if (money/greater-than minimum-base-fee fee) + (money/mul minimum-base-fee 2) + fee))) + +(defn check-base-fee [{:keys [baseFeePerGas]}] + (let [all-base-fees (mapv money/bignumber baseFeePerGas) + current-base-fee (last all-base-fees) + previous-fees (subvec all-base-fees 0 101) + percentiles (calc-percentiles previous-fees [10 20 80])] + {:normal-base-fee (money/to-hex + (recommended-base-fee + current-base-fee + (get percentiles 20) + (get percentiles 80))) + :slow-base-fee (money/to-hex + (slow-base-fee + current-base-fee + (get percentiles 10))) + :fast-base-fee (money/to-hex + (fast-base-fee + current-base-fee + (get percentiles 80))) + :current-base-fee (money/to-hex current-base-fee)})) + +(fx/defn header-fetched + {:events [::header-fetched]} + [_ {:keys [error-callback success-callback fee-history] :as params}] + {::json-rpc/call + [{:method "eth_maxPriorityFeePerGas" + :on-success #(success-callback (merge {:max-priority-fee %} + (check-base-fee fee-history))) + :on-error (if error-callback + #(error-callback %) + #(log/error "Can't fetch header" %))}]}) (re-frame/reg-fx :signing/update-estimated-gas (fn [{:keys [obj success-event error-event]}] - (json-rpc/call - {:method "eth_estimateGas" - :params [obj] - :on-success #(re-frame/dispatch [success-event %]) - :on-error #(re-frame/dispatch [error-event %])}))) + (if (nil? (:data obj)) + (re-frame/dispatch [success-event (money/bignumber 21000)]) + (json-rpc/call + {:method "eth_estimateGas" + :params [obj] + :on-success #(re-frame/dispatch [success-event %]) + :on-error #(re-frame/dispatch [error-event %])})))) diff --git a/src/status_im/signing/keycard.cljs b/src/status_im/signing/keycard.cljs index de2fd23b67..3e427fdba5 100644 --- a/src/status_im/signing/keycard.cljs +++ b/src/status_im/signing/keycard.cljs @@ -25,16 +25,26 @@ (status/hash-typed-data data on-completed)))) (defn prepare-transaction - [{:keys [gas gasPrice data nonce tx-obj]}] - (let [{:keys [from to value chat-id message-id command?]} tx-obj] + [{:keys [gas gasPrice data nonce tx-obj] :as params}] + (let [{:keys [from to value chat-id message-id command? maxPriorityFeePerGas maxFeePerGas]} tx-obj + maxPriorityFeePerGas (or maxPriorityFeePerGas (get params :maxPriorityFeePerGas)) + maxFeePerGas (or maxFeePerGas (get params :maxFeePerGas))] (cond-> {:from from :to to :value value - :gas (str "0x" (abi-spec/number-to-hex gas)) - :gasPrice (str "0x" (abi-spec/number-to-hex gasPrice)) :chat-id chat-id :message-id message-id :command? command?} + maxPriorityFeePerGas + (assoc :maxPriorityFeePerGas (str "0x" (abi-spec/number-to-hex + (js/parseInt maxPriorityFeePerGas)))) + maxFeePerGas + (assoc :maxFeePerGas (str "0x" (abi-spec/number-to-hex + (js/parseInt maxFeePerGas)))) + gas + (assoc :gas (str "0x" (abi-spec/number-to-hex gas))) + gasPrice + (assoc :gasPrice (str "0x" (abi-spec/number-to-hex gasPrice))) data (assoc :data data) nonce @@ -93,7 +103,7 @@ (fx/defn sign-with-keycard {:events [:signing.ui/sign-with-keycard-pressed]} [{:keys [db] :as cofx}] - (let [message (get-in db [:signing/tx :message])] + (let [{:keys [message maxPriorityFeePerGas maxFeePerGas]} (get db :signing/tx)] (fx/merge cofx {:db (-> db diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 8a2ebf681d..766f14ec64 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -165,8 +165,11 @@ (reg-root-key-sub :wallet/refreshing-history? :wallet/refreshing-history?) (reg-root-key-sub :wallet/fetching-error :wallet/fetching-error) (reg-root-key-sub :wallet/non-archival-node :wallet/non-archival-node) -(reg-root-key-sub :wallet/latest-base-fee :wallet/latest-base-fee) -(reg-root-key-sub :wallet/latest-priority-fee :wallet/latest-priority-fee) +(reg-root-key-sub :wallet/current-base-fee :wallet/current-base-fee) +(reg-root-key-sub :wallet/slow-base-fee :wallet/slow-base-fee) +(reg-root-key-sub :wallet/normal-base-fee :wallet/normal-base-fee) +(reg-root-key-sub :wallet/fast-base-fee :wallet/fast-base-fee) +(reg-root-key-sub :wallet/current-priority-fee :wallet/current-priority-fee) (reg-root-key-sub :wallet/transactions-management-enabled? :wallet/transactions-management-enabled?) ;;commands (reg-root-key-sub :commands/select-account :commands/select-account) @@ -2005,7 +2008,7 @@ (re-frame/subscribe [:ethereum/native-currency]) (re-frame/subscribe [:ethereum/chain-keyword])]) (fn [[transactions native-currency chain-keyword] [_ hash _]] - (let [{:keys [gas-used gas-price hash timestamp type] + (let [{:keys [gas-used gas-price fee-cap tip-cap hash timestamp type] :as transaction} (get transactions hash) native-currency-text (name (or (:symbol-display native-currency) @@ -2021,6 +2024,14 @@ (money/wei->str :gwei gas-price) "-") + :fee-cap-gwei (if fee-cap + (money/wei->str :gwei + fee-cap) + "-") + :tip-cap-gwei (if tip-cap + (money/wei->str :gwei + tip-cap) + "-") :date (datetime/timestamp->long-date timestamp)} (if (= type :unsigned) {:block (i18n/label :not-applicable) @@ -2587,14 +2598,22 @@ (re-frame/reg-sub :signing/priority-fee-suggestions-range - :<- [:wallet/latest-priority-fee] - (fn [latest-fee] - [0 (->> latest-fee - money/bignumber - (money/wei-> :gwei) - (money/mul (money/bignumber 2)) - money/to-fixed - js/parseFloat)])) + :<- [:wallet/current-priority-fee] + :<- [:wallet/slow-base-fee] + :<- [:wallet/normal-base-fee] + :<- [:wallet/fast-base-fee] + (fn [[latest-tip slow normal fast]] + (reduce + (fn [acc [k fees]] + (assoc acc k (reduce + (fn [acc [k fee]] + (assoc acc k (-> fee + money/wei->gwei + (money/to-fixed 2)))) + {} + fees))) + {} + (signing.gas/get-fee-options latest-tip slow normal fast)))) (re-frame/reg-sub :signing/phrase diff --git a/src/status_im/ui/screens/signing/sheets.cljs b/src/status_im/ui/screens/signing/sheets.cljs index 57001a6c92..bba88087ff 100644 --- a/src/status_im/ui/screens/signing/sheets.cljs +++ b/src/status_im/ui/screens/signing/sheets.cljs @@ -6,7 +6,6 @@ [quo.core :as quo] [quo.design-system.colors :as quo-colors] [status-im.ui.components.colors :as colors] - [status-im.ui.components.slider :as slider] [status-im.utils.money :as money] [status-im.ui.components.icons.icons :as icons] [clojure.string :as clojure.string] @@ -69,12 +68,15 @@ (declare fee-bottom-sheet-eip1559) -(defn fee-bottom-sheet-eip1559-custom [fee-display-symbol] +(defn fee-bottom-sheet-eip1559-custom [_ #_fee-display-symbol] (let [{gas-edit :gas max-fee-per-gas-edit :maxFeePerGas max-priority-fee-per-gas-edit :maxPriorityFeePerGas} @(re-frame/subscribe [:signing/edit-fee]) - base-fee @(re-frame/subscribe [:wallet/latest-base-fee]) + error? (some :error [gas-edit + max-fee-per-gas-edit + max-priority-fee-per-gas-edit]) + base-fee @(re-frame/subscribe [:wallet/current-base-fee]) [fee-currency fiat-currency price] @(re-frame/subscribe [:signing/currencies]) fee-eth @@ -123,7 +125,7 @@ {:label (i18n/label :t/per-gas-tip-limit) :error (or (:error max-priority-fee-per-gas-edit) (get-in max-priority-fee-per-gas-edit [:fee-error :label])) - :default-value (:value max-priority-fee-per-gas-edit) + :default-value (str (:value max-priority-fee-per-gas-edit)) :on-change-text #(re-frame/dispatch [:signing.edit-fee.ui/edit-value :maxPriorityFeePerGas %]) :show-cancel false :after {:component [quo/text @@ -137,7 +139,7 @@ {:label (i18n/label :t/per-gas-price-limit) :error (or (:error max-fee-per-gas-edit) (get-in max-fee-per-gas-edit [:fee-error :label])) - :default-value (:value max-fee-per-gas-edit) + :default-value (str (:value max-fee-per-gas-edit)) :on-change-text #(re-frame/dispatch [:signing.edit-fee.ui/edit-value :maxFeePerGas %]) :show-cancel false :after {:component [quo/text @@ -176,66 +178,75 @@ :justify-content :space-between}} [quo/button {:type :secondary - :on-press #(re-frame/dispatch - [:bottom-sheet/show-sheet - {:content (fn [] - [fee-bottom-sheet-eip1559 fee-display-symbol]) - :content-height 270}])} - (i18n/label :t/see-suggestions)] + :accessibility-label :see-fee-suggestions + ;;:on-press + #_(re-frame/dispatch + [:bottom-sheet/show-sheet + {:content (fn [] + [fee-bottom-sheet-eip1559 fee-display-symbol]) + :content-height 270}])} + "" #_(i18n/label :t/see-suggestions)] [quo/button - {:type :primary + {:type :primary + :disabled error? :on-press #(re-frame/dispatch [:signing.edit-fee.ui/submit]) - :theme :accent} + :theme :accent} (i18n/label :t/save)]]])) (defn fee-bottom-sheet-eip1559 [fee-display-symbol] (let [{priority-fee-edit :maxPriorityFeePerGas - fee-edit :maxFeePerGas - gas :gas} + option :selected-fee-option + fee-edit :maxFeePerGas} @(re-frame/subscribe [:signing/edit-fee]) - [min-value max-value] - @(re-frame/subscribe [:signing/priority-fee-suggestions-range]) - [fee-currency fiat-currency price] @(re-frame/subscribe [:signing/currencies]) - fee-eth - (if (and (:value-number gas) - (:value-number fee-edit)) - (money/mul - (money/wei->ether - (money/->wei :gwei (:value-number fee-edit))) - (:value-number gas)) - (money/bignumber 0))] + {:keys [normal fast slow]} + @(re-frame/subscribe [:signing/priority-fee-suggestions-range])] [react/view [react/view {:style {:margin-horizontal 16 :margin-top 8}} - [react/text {:style {:typography :title-bold}} (i18n/label :t/max-priority-fee)] - [react/text {:style {:color (quo-colors/get-color :text-02) - :margin-top 12}} - (i18n/label :t/miners-higher-fee)] - [quo/text {:style {:margin-horizontal 8 - :margin-top 24} - :size :large} + [react/text {:style {:typography :title-bold}} (i18n/label :t/fee-options)] + [react/text {:style {:margin-top 12} + :accessibility-label :slow-fee + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :slow])} (clojure.string/join " " - [(money/to-fixed fee-eth 6) - fee-currency - "•" - (money/to-fixed (money/mul fee-eth (money/bignumber (or price 0))) 2) - fiat-currency])] - [slider/animated-slider - {:style {:margin-horizontal 5 - :margin-vertical 4} - :minimumValue min-value - :value (js/parseFloat (:value priority-fee-edit)) - :onValueChange #(re-frame/dispatch [:signing.edit-fee.ui/set-priority-fee (str %)]) - :maximumValue max-value - :minimumTrackTintColor (quo-colors/get-color :icon-04)}] - [react/view - {:style {:margin-horizontal 8 - :flex-direction :row - :align-items :center - :justify-content :space-between}} - [react/text {:style {:color (quo-colors/get-color :text-02)}} (i18n/label :t/slow)] - [react/text {:style {:color (quo-colors/get-color :text-02)}} (i18n/label :t/optimal)] - [react/text {:style {:color (quo-colors/get-color :text-02)}} (i18n/label :t/fast)]]] + [(str (i18n/label :t/slow) ":") + (str (:base-fee slow) " gwei") + (str (:tip slow) " gwei") + (when (= :slow option) + "<- selected")])] + [react/text {:style {:margin-top 12} + :accessibility-label :normal-fee + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :normal])} + (clojure.string/join + " " + [(str (i18n/label :t/normal) ":") + (str (:base-fee normal) " gwei") + (str (:tip normal) " gwei") + (when (or (nil? option) + (= :normal option)) + "<- selected")])] + [react/text {:style {:margin-top 12} + :accessibility-label :fast-fee + :on-press #(re-frame/dispatch [:signing.edit-fee.ui/set-option :fast])} + (clojure.string/join + " " + [(str (i18n/label :t/fast) ":") + (str (:base-fee fast) " gwei") + (str (:tip fast) " gwei") + (when (= :fast option) + "<- selected")])] + (when (= :custom option) + [react/text {:style {:margin-top 12}} + (clojure.string/join + " " + [(str (i18n/label :t/custom) ":") + (str (-> fee-edit + :value-number + (money/to-fixed 2)) " gwei") + (str (-> priority-fee-edit + :value-number + (money/to-fixed 2)) " gwei") + (when (= :custom option) + "<- selected")])])] [react/view {:style {:margin-left 12 :margin-right 16 @@ -245,6 +256,7 @@ :justify-content :space-between} [quo/button {:type :secondary + :accessibility-label :set-custom-fee :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content (fn [] @@ -253,17 +265,18 @@ (i18n/label :t/set-custom-fee)] [quo/button {:type :primary + :accessibility-label :save-custom-fee :theme :accent :on-press #(re-frame/dispatch [:signing.edit-fee.ui/submit])} (i18n/label :t/save)]]])) (defn gwei [val] - (str val " " (i18n/label :t/gwei))) + (str (money/to-fixed val 2) " " (i18n/label :t/gwei))) (defn fees-warning [] - (let [base-fee @(re-frame/subscribe [:wallet/latest-base-fee]) + (let [base-fee @(re-frame/subscribe [:wallet/current-base-fee]) base-fee-gwei (money/wei-> :gwei (money/bignumber base-fee)) - priority-fee @(re-frame/subscribe [:wallet/latest-priority-fee]) + priority-fee @(re-frame/subscribe [:wallet/current-priority-fee]) priority-fee-gwei (money/wei-> :gwei (money/bignumber priority-fee)) {priority-fee-edit :maxPriorityFeePerGas fee-edit :maxFeePerGas} @@ -293,19 +306,21 @@ :justify-content :space-between :margin-horizontal 32}} [react/text (i18n/label :t/current-base-fee)] - [react/text (gwei (money/to-fixed base-fee-gwei 2))]] + [react/text (gwei base-fee-gwei)]] [react/view {:style {:flex-direction :row :justify-content :space-between :margin-horizontal 32}} [react/text (i18n/label :t/current-minimum-tip)] - [react/text (gwei gas/minimum-priority-fee)]] - [react/view - {:style {:flex-direction :row - :justify-content :space-between - :margin-horizontal 32}} - [react/text (i18n/label :t/current-average-tip)] - [react/text (gwei (money/to-fixed priority-fee-gwei 2))]] + [react/text (gwei (gas/get-minimum-priority-fee priority-fee))]] + ;;TODO(rasom): we can uncomment it once it will be clear which value can be + ;;used as "average" here + #_[react/view + {:style {:flex-direction :row + :justify-content :space-between + :margin-horizontal 32}} + [react/text (i18n/label :t/current-average-tip)] + [react/text (gwei (money/to-fixed priority-fee-gwei 2))]] [react/view {:margin-vertical 16 :height 1 :background-color colors/gray-lighter}] @@ -317,7 +332,7 @@ [react/text {:style {:color (quo-colors/get-color :negative-01)}} (i18n/label :t/your-tip-limit)] [react/text {:style {:color (quo-colors/get-color :negative-01)}} - (gwei (money/to-fixed (:value-number priority-fee-edit) 2))]] + (gwei (:value-number priority-fee-edit))]] [react/view {:style {:flex-direction :row :justify-content :space-between @@ -325,7 +340,7 @@ [react/text {:style {:color (quo-colors/get-color :negative-01)}} (i18n/label :t/your-price-limit)] [react/text {:style {:color (quo-colors/get-color :negative-01)}} - (gwei (money/to-fixed (:value-number fee-edit) 2))]] + (gwei (:value-number fee-edit))]] [react/view {:style {:background-color colors/gray-lighter :padding-horizontal 32 @@ -335,12 +350,12 @@ {:style {:flex-direction :row :justify-content :space-between}} [react/text (i18n/label :t/suggested-min-tip)] - [react/text (gwei gas/minimum-priority-fee)]] + [react/text (gwei priority-fee-gwei)]] [react/view {:style {:flex-direction :row :justify-content :space-between}} [react/text (i18n/label :t/suggested-price-limit)] - [react/text (gwei (money/to-fixed (money/add base-fee-gwei priority-fee-gwei) 2))]]] + [react/text (gwei (money/add base-fee-gwei priority-fee-gwei))]]] [react/view {:style {:align-items :center :justify-content :center diff --git a/src/status_im/ui/screens/signing/views.cljs b/src/status_im/ui/screens/signing/views.cljs index ad2d2a351d..c0c26a3d21 100644 --- a/src/status_im/ui/screens/signing/views.cljs +++ b/src/status_im/ui/screens/signing/views.cljs @@ -25,8 +25,7 @@ [status-im.ui.screens.keycard.pin.views :as pin.views] [status-im.ui.components.bottom-panel.views :as bottom-panel] [status-im.utils.utils :as utils] - [reagent.core :as reagent] - [status-im.signing.eip1559 :as eip1559])) + [reagent.core :as reagent])) (defn separator [] [react/view {:height 1 :background-color colors/gray-lighter}]) @@ -382,13 +381,18 @@ [react/text {:style {:color colors/black}} (i18n/format-currency converted-fee-value (:code wallet-currency))]) [react/text {:style {:color colors/gray}} (str " " (:code wallet-currency))]])) - :on-press #(re-frame/dispatch - [:signing.ui/open-fee-sheet - {:content (fn [] - (if (eip1559/sync-enabled?) - [sheets/fee-bottom-sheet-eip1559 fee-display-symbol] - [sheets/fee-bottom-sheet fee-display-symbol])) - :content-height 270}])}]))) + :on-press #(re-frame/dispatch + [:signing.ui/open-fee-sheet + {:content (fn [] + [sheets/fee-bottom-sheet-eip1559-custom fee-display-symbol]) + :content-height 270}]) + #_(re-frame/dispatch + [:signing.ui/open-fee-sheet + {:content (fn [] + (if (eip1559/sync-enabled?) + [sheets/fee-bottom-sheet-eip1559 fee-display-symbol] + [sheets/fee-bottom-sheet fee-display-symbol])) + :content-height 270}])}]))) (views/defview network-item [] (views/letsubs [network-name [:network-name]] diff --git a/src/status_im/ui/screens/wallet/transactions/views.cljs b/src/status_im/ui/screens/wallet/transactions/views.cljs index 36a429d552..bc5221aacb 100644 --- a/src/status_im/ui/screens/wallet/transactions/views.cljs +++ b/src/status_im/ui/screens/wallet/transactions/views.cljs @@ -243,6 +243,7 @@ from from-wallet from-contact to to-wallet to-contact gas-limit gas-price-gwei gas-price-eth gas-used + fee-cap-gwei tip-cap-gwei cost nonce data]}] [react/view {:style styles/details-block} [details-list-row :t/block block] @@ -261,6 +262,8 @@ to])] [details-list-row :t/gas-limit [{:monospace true} gas-limit]] [details-list-row :t/gas-price gas-price-gwei [{:monospace false} gas-price-eth]] + [details-list-row :t/fee-cap fee-cap-gwei] + [details-list-row :t/tip-cap tip-cap-gwei] [details-list-row :t/gas-used gas-used] [details-list-row :t/cost-fee [{:monospace false} cost]] [details-list-row :t/nonce nonce] diff --git a/src/status_im/utils/money.cljs b/src/status_im/utils/money.cljs index 5ce5508d67..9d9f489aed 100644 --- a/src/status_im/utils/money.cljs +++ b/src/status_im/utils/money.cljs @@ -39,10 +39,10 @@ (defn greater-than [bn1 bn2] - (.greaterThan bn1 bn2)) + (.greaterThan ^js bn1 bn2)) (defn sub [bn1 bn2] - (.sub bn1 bn2)) + (.sub ^js bn1 bn2)) (defn valid? [^js bn] (when bn @@ -103,6 +103,9 @@ (defn wei->ether [n] (wei-> :eth n)) +(defn wei->gwei [n] + (wei-> :gwei n)) + (defn ether->wei [^js bn] (when bn (.times bn ^js (bignumber 1e18)))) @@ -171,7 +174,16 @@ str)) (defn add [bn1 n2] - (.add bn1 n2)) + (.add ^js bn1 n2)) (defn mul [bn1 bn2] - (.mul bn1 bn2)) + (.mul ^js bn1 bn2)) + +(defn mul-and-round [bn1 bn2] + (.round (.mul ^js bn1 bn2) 0)) + +(defn div [bn1 bn2] + (.dividedBy ^js bn1 bn2)) + +(defn div-and-round [bn1 bn2] + (.round (.dividedBy ^js bn1 bn2) 0)) diff --git a/src/status_im/wallet/core.cljs b/src/status_im/wallet/core.cljs index a8c51f0ab1..30b948cea0 100644 --- a/src/status_im/wallet/core.cljs +++ b/src/status_im/wallet/core.cljs @@ -28,6 +28,7 @@ [status-im.async-storage.core :as async-storage] [status-im.popover.core :as popover.core] [status-im.signing.eip1559 :as eip1559] + [status-im.signing.gas :as signing.gas] [clojure.set :as clojure.set])) (defn get-balance @@ -380,23 +381,40 @@ (fx/defn wallet-send-gas-price-success {:events [:wallet.send/update-gas-price-success]} - [{db :db} price] + [{db :db} tx-entry price] (if (eip1559/sync-enabled?) - (let [{:keys [base-fee max-priority-fee]} price - max-priority-fee-bn (money/bignumber max-priority-fee)] + (let [{:keys [slow-base-fee normal-base-fee fast-base-fee + current-base-fee max-priority-fee]} + price + max-priority-fee-bn (money/with-precision + (signing.gas/get-suggested-tip max-priority-fee) + 0) + fee-cap (-> normal-base-fee + money/bignumber + (money/add max-priority-fee-bn) + money/to-hex) + tip-cap (money/to-hex max-priority-fee-bn)] {:db (-> db - (update :wallet/prepare-transaction assoc - :maxFeePerGas (money/to-hex (money/add max-priority-fee-bn base-fee)) - :maxPriorityFeePerGas max-priority-fee) - (assoc :wallet/latest-base-fee base-fee - :wallet/latest-priority-fee max-priority-fee))}) - {:db (assoc-in db [:wallet/prepare-transaction :gasPrice] price)})) + (update tx-entry assoc + :maxFeePerGas fee-cap + :maxPriorityFeePerGas tip-cap) + (assoc :wallet/current-base-fee current-base-fee + :wallet/normal-base-fee normal-base-fee + :wallet/slow-base-fee slow-base-fee + :wallet/fast-base-fee fast-base-fee + :wallet/current-priority-fee max-priority-fee) + (assoc-in [:signing/edit-fee :gas-price-loading?] false))}) + {:db (-> db + (assoc-in [:wallet/prepare-transaction :gasPrice] price) + (assoc-in [:signing/edit-fee :gas-price-loading?] false))})) (fx/defn set-max-amount {:events [:wallet.send/set-max-amount]} [{:keys [db]} {:keys [amount decimals symbol]}] (let [^js gas (money/bignumber 21000) - ^js gasPrice (get-in db [:wallet/prepare-transaction :gasPrice]) + ^js gasPrice (or + (get-in db [:wallet/prepare-transaction :maxFeePerGas]) + (get-in db [:wallet/prepare-transaction :gasPrice])) ^js fee (when gasPrice (.times gas gasPrice)) amount-text (if (= :ETH symbol) (when (and fee (money/sufficient-funds? fee amount)) @@ -478,7 +496,9 @@ {:events [::recipient-address-resolved]} [{:keys [db]} address] {:db (assoc-in db [:wallet/prepare-transaction :to :address] address) - :signing/update-gas-price {:success-event :wallet.send/update-gas-price-success + :signing/update-gas-price {:success-callback + #(re-frame/dispatch + [:wallet.send/update-gas-price-success :wallet/prepare-transaction %]) :network-id (get-in (ethereum/current-network db) [:config :NetworkId])}}) @@ -533,7 +553,9 @@ :symbol :ETH :from-chat? false}) :dispatch [:open-modal :prepare-send-transaction] - :signing/update-gas-price {:success-event :wallet.send/update-gas-price-success + :signing/update-gas-price {:success-callback + #(re-frame/dispatch + [:wallet.send/update-gas-price-success :wallet/prepare-transaction %]) :network-id (get-in (ethereum/current-network db) [:config :NetworkId])}}) diff --git a/translations/en.json b/translations/en.json index 4aeaa7f863..763c63105f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1648,5 +1648,9 @@ "category": "Category", "edit-chats": "Edit chats", "hide": "Hide", - "account-is-used": "The account is being used with Dapps in the browser." + "account-is-used": "The account is being used with Dapps in the browser.", + "normal": "Normal", + "fee-options": "Suggested fee options", + "fee-cap": "Fee cap", + "tip-cap": "Tip cap" }