Fix send no bonder fee included (#21116)

This commit is contained in:
Lungu Cristian 2024-11-14 18:47:42 +02:00 committed by GitHub
parent 3f12c04684
commit eb3ae3c928
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 518 additions and 124 deletions

View File

@ -55,26 +55,36 @@
theme))}])])) theme))}])]))
(defn- left-title (defn- left-title
[{:keys [title blur?]}] [{:keys [title blur? title-icon]}]
(let [theme (quo.theme/use-theme)] (let [theme (quo.theme/use-theme)]
[rn/view {:style style/title-container} [rn/view {:style style/title-container}
[text/text [text/text
{:weight :regular {:weight :regular
:size :paragraph-2 :size :paragraph-2
:style (style/title blur? theme)} :style (style/title blur? theme)}
title]])) title]
(when title-icon
[icons/icon title-icon
{:accessibility-label :title-icon
:size 12
:color (if blur?
colors/neutral-40
(colors/theme-colors colors/neutral-50
colors/neutral-40
theme))}])]))
(defn- left-side (defn- left-side
"The description can either be given as a string `subtitle-type` or a component `custom-subtitle`" "The description can either be given as a string `subtitle-type` or a component `custom-subtitle`"
[{:keys [title status size blur? custom-subtitle icon subtitle subtitle-type subtitle-color icon-color [{:keys [title status size blur? custom-subtitle icon subtitle subtitle-type subtitle-color icon-color
customization-color network-image emoji] customization-color network-image emoji title-icon]
:as props}] :as props}]
(let [theme (quo.theme/use-theme)] (let [theme (quo.theme/use-theme)]
[rn/view {:style style/left-side} [rn/view {:style style/left-side}
[left-title [rn/view
{:title title [left-title
:blur? blur? {:title title
:theme theme}] :title-icon title-icon
:blur? blur?}]]
(if (= status :loading) (if (= status :loading)
[left-loading [left-loading
{:size size {:size size
@ -132,6 +142,7 @@
[:subtitle {:optional true} [:maybe [:or :string :double]]] [:subtitle {:optional true} [:maybe [:or :string :double]]]
[:custom-subtitle {:optional true} [:maybe fn?]] [:custom-subtitle {:optional true} [:maybe fn?]]
[:icon {:optional true} [:maybe :keyword]] [:icon {:optional true} [:maybe :keyword]]
[:title-icon {:optional true} [:maybe :keyword]]
[:emoji {:optional true} [:maybe :string]] [:emoji {:optional true} [:maybe :string]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]] [:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:network-image {:optional true} [:maybe :schema.common/image-source]] [:network-image {:optional true} [:maybe :schema.common/image-source]]

View File

@ -309,6 +309,8 @@
(def ^:const token-sort-priority {"SNT" 1 "STT" 1 "ETH" 2 "DAI" 3}) (def ^:const token-sort-priority {"SNT" 1 "STT" 1 "ETH" 2 "DAI" 3})
(def ^:const token-display-precision 6)
(def ^:const dapp-permission-contact-code "contact-code") (def ^:const dapp-permission-contact-code "contact-code")
(def ^:const dapp-permission-web3 "web3") (def ^:const dapp-permission-web3 "web3")
(def ^:const dapp-permission-qr-code "qr-code") (def ^:const dapp-permission-qr-code "qr-code")

View File

@ -25,6 +25,12 @@
{:key :i/copy} {:key :i/copy}
{:key nil {:key nil
:value "None"}]} :value "None"}]}
{:type :select
:key :title-icon
:options [{:key :i/chevron-right}
{:key :i/copy}
{:key nil
:value "None"}]}
{:type :select {:type :select
:key :right-content :key :right-content
:options [{:key nil :options [{:key nil

View File

@ -457,7 +457,7 @@
(defn transaction-path (defn transaction-path
[{:keys [from-address to-address token-id-from token-address token-id-to route data [{:keys [from-address to-address token-id-from token-address token-id-to route data
slippage-percentage eth-transfer?]}] slippage-percentage eth-transfer?]}]
(let [{:keys [bridge-name amount-in bonder-fees from (let [{:keys [bridge-name amount-in from
to]} route to]} route
tx-data (transaction-data {:from-address from-address tx-data (transaction-data {:from-address from-address
:to-address to-address :to-address to-address
@ -465,6 +465,7 @@
:route route :route route
:data data :data data
:eth-transfer? eth-transfer?}) :eth-transfer? eth-transfer?})
bonder-fees (-> route :bounder-fees money/to-string)
to-chain-id (:chain-id to) to-chain-id (:chain-id to)
from-chain-id (:chain-id from)] from-chain-id (:chain-id from)]
(cond-> {:BridgeName bridge-name (cond-> {:BridgeName bridge-name

View File

@ -233,13 +233,12 @@
(defn new->old-route-path (defn new->old-route-path
[new-path] [new-path]
(let [bonder-fees (:tx-bonder-fees new-path) (let [bonder-fees (-> new-path :tx-bonder-fees money/bignumber)
token-fees (+ (money/wei->ether bonder-fees) token-fees (-> new-path :tx-token-fees money/bignumber)]
(money/wei->ether (:tx-token-fees new-path)))]
{:from (:from-chain new-path) {:from (:from-chain new-path)
:amount-in-locked (:amount-in-locked new-path) :amount-in-locked (:amount-in-locked new-path)
:amount-in (:amount-in new-path) :amount-in (:amount-in new-path)
:max-amount-in "0x0" :max-amount-in (:max-amount-in new-path)
:gas-fees {:gas-price "0" :gas-fees {:gas-price "0"
:base-fee (send-utils/convert-to-gwei (:tx-base-fee :base-fee (send-utils/convert-to-gwei (:tx-base-fee
new-path) new-path)

View File

@ -104,16 +104,19 @@
:wallet/wallet-send-tx-type :tx/send :wallet/wallet-send-tx-type :tx/send
:wallet/wallet-send-fee-fiat-formatted "$5,00" :wallet/wallet-send-fee-fiat-formatted "$5,00"
:wallet/sending-collectible? false :wallet/sending-collectible? false
:wallet/total-amount (money/bignumber "250")}) :wallet/send-total-amount-formatted "250 ETH"
:wallet/total-amount (money/bignumber "250")
:wallet/bridge-to-network-details nil
:wallet/send-amount-fixed ""
:wallet/send-display-token-decimals 5})
(h/describe "Send > input amount screen" (h/describe "Send > input amount screen"
(h/setup-restorable-re-frame) (h/setup-restorable-re-frame)
(h/test "Default render" (h/test "Default render"
(h/setup-subs sub-mocks) (h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 2))
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 2 {:limit-crypto (money/bignumber 250)
:limit-crypto (money/bignumber 250)
:initial-crypto-currency? false}]) :initial-crypto-currency? false}])
(h/is-truthy (h/get-by-text "0")) (h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "USD")) (h/is-truthy (h/get-by-text "USD"))
@ -122,12 +125,11 @@
(h/is-disabled (h/get-by-label-text :button-one))) (h/is-disabled (h/get-by-label-text :button-one)))
(h/test "Fill token input and confirm" (h/test "Fill token input and confirm"
(h/setup-subs sub-mocks) (h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 10))
(let [on-confirm (h/mock-fn)] (let [on-confirm (h/mock-fn)]
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 10 {:limit-crypto (money/bignumber 1000)
:limit-crypto (money/bignumber 1000)
:on-confirm on-confirm :on-confirm on-confirm
:initial-crypto-currency? true}]) :initial-crypto-currency? true}])
@ -147,10 +149,9 @@
(h/was-called on-confirm))))))) (h/was-called on-confirm)))))))
(h/test "Try to fill more than limit" (h/test "Try to fill more than limit"
(h/setup-subs sub-mocks) (h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 1))
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 1 {:limit-crypto (money/bignumber 1)}])
:limit-crypto (money/bignumber 1)}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2)) (h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))
@ -159,11 +160,10 @@
(h/is-truthy (h/get-by-label-text :container-error))) (h/is-truthy (h/get-by-label-text :container-error)))
(h/test "Switch from crypto to fiat and check limit" (h/test "Switch from crypto to fiat and check limit"
(h/setup-subs sub-mocks) (h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 1))
(h/render-with-theme-provider [input-amount/view (h/render-with-theme-provider [input-amount/view
{:crypto-decimals 1 {:limit-crypto (money/bignumber 10)
:limit-crypto (money/bignumber 10) :on-confirm #()}])
:on-confirm #()}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9)) (h/fire-event :press (h/query-by-label-text :keyboard-key-9))

View File

@ -0,0 +1,67 @@
(ns status-im.contexts.wallet.send.input-amount.estimated-fees
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im.common.not-implemented :as not-implemented]
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- bonder-fee-info-sheet
[]
[:<>
[quo/drawer-top
{:title (i18n/label :t/understanding-bonder-fees-title)}]
[rn/view {:style {:padding-horizontal 20}}
[quo/text
{:weight :regular
:size :paragraph-2}
(i18n/label :t/understanding-bonder-fees-description)]]])
(defn- show-bonder-fee-info
[]
(rf/dispatch
[:show-bottom-sheet {:content bonder-fee-info-sheet}]))
(defn- received-amount
[{:keys [loading-routes?]}]
(let [amount (rf/sub [:wallet/send-total-amount-formatted])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
{:keys [full-name]} (rf/sub [:wallet/bridge-to-network-details])]
[quo/data-item
(cond-> {:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/recipient-gets)
:subtitle amount}
(= tx-type :tx/bridge)
(assoc
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:on-press show-bonder-fee-info))]))
(defn view
[{:keys [loading-routes? fees]}]
[rn/view {:style style/estimated-fees-container}
(when (ff/enabled? ::ff/wallet.advanced-sending)
[rn/view {:style style/estimated-fees-content-container}
[quo/button
{:icon-only? true
:type :outline
:size 32
:inner-style {:opacity 1}
:accessibility-label :advanced-button
:disabled? loading-routes?
:on-press not-implemented/alert}
:i/advanced]])
[quo/data-item
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/fees)
:subtitle fees}]
[received-amount {:loading-routes? loading-routes?}]])

View File

@ -11,45 +11,17 @@
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher] [status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.asset-list.view :as asset-list] [status-im.contexts.wallet.common.asset-list.view :as asset-list]
[status-im.contexts.wallet.common.utils :as utils] [status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.input-amount.estimated-fees :as estimated-fees]
[status-im.contexts.wallet.send.input-amount.style :as style] [status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.contexts.wallet.send.routes.view :as routes] [status-im.contexts.wallet.send.routes.view :as routes]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token] [status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert] [status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
[status-im.feature-flags :as ff]
[status-im.setup.hot-reload :as hot-reload] [status-im.setup.hot-reload :as hot-reload]
[utils.debounce :as debounce] [utils.debounce :as debounce]
[utils.i18n :as i18n] [utils.i18n :as i18n]
[utils.money :as money] [utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn- estimated-fees
[{:keys [loading-routes? fees amount]}]
[rn/view {:style style/estimated-fees-container}
(when (ff/enabled? ::ff/wallet.advanced-sending)
[rn/view {:style style/estimated-fees-content-container}
[quo/button
{:icon-only? true
:type :outline
:size 32
:inner-style {:opacity 1}
:accessibility-label :advanced-button
:disabled? loading-routes?
:on-press #(js/alert "Not implemented yet")}
:i/advanced]])
[quo/data-item
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/fees)
:subtitle fees}]
[quo/data-item
{:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/recipient-gets)
:subtitle amount}]])
(defn- every-network-value-is-zero? (defn- every-network-value-is-zero?
[sender-network-values] [sender-network-values]
(every? (fn [{:keys [total-amount]}] (every? (fn [{:keys [total-amount]}]
@ -165,7 +137,6 @@
;; for component tests only ;; for component tests only
[{default-on-confirm :on-confirm [{default-on-confirm :on-confirm
default-limit-crypto :limit-crypto default-limit-crypto :limit-crypto
default-crypto-decimals :crypto-decimals
on-navigate-back :on-navigate-back on-navigate-back :on-navigate-back
button-one-label :button-one-label button-one-label :button-one-label
button-one-props :button-one-props button-one-props :button-one-props
@ -199,17 +170,13 @@
:market-values-per-currency :market-values-per-currency
currency currency
:price) :price)
token-decimals (-> token token-decimals (rf/sub [:wallet/send-display-token-decimals])
utils/token-usd-price
utils/one-cent-value
utils/calc-max-crypto-decimals)
[input-state set-input-state] (rn/use-state controlled-input/init-state) [input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all) clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol]) currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?]) loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route]) route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm) on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals)
max-limit (if crypto-currency? max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents (utils/cut-crypto-decimals-to-fit-usd-cents
token-balance token-balance
@ -221,15 +188,8 @@
(controlled-input/input-error input-state))) (controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency? amount-in-crypto (if crypto-currency?
input-value input-value
(number/remove-trailing-zeroes (rf/sub [:wallet/send-amount-fixed
(.toFixed (/ input-value conversion-rate) (/ input-value conversion-rate)]))
crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes
(.toFixed total-amount-receiver
(min token-decimals 6)))
" "
token-symbol)
show-select-asset-sheet #(rf/dispatch show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet [:show-bottom-sheet
{:content (fn [] {:content (fn []
@ -382,10 +342,9 @@
[not-enough-asset]) [not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route)) (when (or (and (not no-routes-found?) (or loading-routes? route))
not-enough-asset?) not-enough-asset?)
[estimated-fees [estimated-fees/view
{:loading-routes? loading-routes? {:loading-routes? loading-routes?
:fees fee-formatted :fees fee-formatted}])
:amount amount-text}])
(when show-no-routes? (when show-no-routes?
[no-routes-found]) [no-routes-found])
[quo/bottom-actions [quo/bottom-actions
@ -417,7 +376,7 @@
:delete-key? true :delete-key? true
:on-press (fn [c] :on-press (fn [c]
(let [new-text (str input-value c) (let [new-text (str input-value c)
max-decimals (if crypto-currency? crypto-decimals 2) max-decimals (if crypto-currency? token-decimals 2)
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$") regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
regex (re-pattern regex-pattern)] regex (re-pattern regex-pattern)]
(when (re-matches regex new-text) (when (re-matches regex new-text)

View File

@ -247,20 +247,22 @@
{chain-id :chain-id {chain-id :chain-id
network-value-type :type network-value-type :type
total-amount :total-amount}] total-amount :total-amount}]
(let [status (cond (and (= network-value-type :not-available) (let [status (cond (and (= network-value-type :not-available)
loading-routes? loading-routes?
token-not-supported-in-receiver-networks?) token-not-supported-in-receiver-networks?)
:loading :loading
(= network-value-type :not-available) (= network-value-type :not-available)
:disabled :disabled
:else network-value-type)] :else network-value-type)
amount-formatted (-> (rf/sub [:wallet/send-amount-fixed total-amount])
(str " " token-symbol))]
[rn/view [rn/view
{:key (str (if receiver? "to" "from") "-" chain-id) {:key (str (if receiver? "to" "from") "-" chain-id)
:style {:margin-top (if (pos? index) 11 7.5)}} :style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge [quo/network-bridge
{:amount (if (= network-value-type :not-available) {:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available) (i18n/label :t/not-available)
(str total-amount " " token-symbol)) amount-formatted)
:network (network-utils/id->network chain-id) :network (network-utils/id->network chain-id)
:status status :status status
:on-press #(when (not loading-routes?) :on-press #(when (not loading-routes?)

View File

@ -165,10 +165,11 @@
:subtitle subtitle}]) :subtitle subtitle}])
(defn- transaction-details (defn- transaction-details
[{:keys [estimated-time-min max-fees token-display-name amount to-network route [{:keys [estimated-time-min max-fees to-network route
transaction-type]}] transaction-type]}]
(let [route-loaded? (and route (seq route)) (let [route-loaded? (and route (seq route))
loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])] loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
amount (rf/sub [:wallet/send-total-amount-formatted])]
[rn/view [rn/view
{:style (style/details-container {:style (style/details-container
{:loading-suggested-routes? loading-suggested-routes? {:loading-suggested-routes? loading-suggested-routes?
@ -189,7 +190,7 @@
(i18n/label :t/bridged-to (i18n/label :t/bridged-to
{:network (:abbreviated-name to-network)}) {:network (:abbreviated-name to-network)})
(i18n/label :t/recipient-gets)) (i18n/label :t/recipient-gets))
:subtitle (str amount " " token-display-name)}]] :subtitle amount}]]
:else :else
[quo/text {:style {:align-self :center}} [quo/text {:style {:align-self :center}}
(i18n/label :t/no-routes-found-confirmation)])])) (i18n/label :t/no-routes-found-confirmation)])]))
@ -223,7 +224,6 @@
bridge-to-chain-id])) bridge-to-chain-id]))
loading-suggested-routes? (rf/sub loading-suggested-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?]) [:wallet/wallet-send-loading-suggested-routes?])
total-amount-receiver (rf/sub [:wallet/total-amount true])
from-account-props {:customization-color account-color from-account-props {:customization-color account-color
:size 32 :size 32
:emoji (:emoji account) :emoji (:emoji account)
@ -248,8 +248,6 @@
[transaction-details [transaction-details
{:estimated-time-min estimated-time-min {:estimated-time-min estimated-time-min
:max-fees fee-formatted :max-fees fee-formatted
:token-display-name token-symbol
:amount total-amount-receiver
:to-network bridge-to-network :to-network bridge-to-network
:theme theme :theme theme
:route route :route route

View File

@ -1,9 +1,7 @@
(ns status-im.contexts.wallet.send.utils (ns status-im.contexts.wallet.send.utils
(:require (:require
[native-module.core :as native-module]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.hex :as utils.hex]
[utils.money :as money])) [utils.money :as money]))
(defn amount-in-hex (defn amount-in-hex
@ -43,21 +41,67 @@
[route] [route]
(money/wei->ether (reduce money/add (map calculate-gas-fee route)))) (money/wei->ether (reduce money/add (map calculate-gas-fee route))))
(defn- path-amount-in
[path]
(-> path :amount-in money/from-hex))
(defn- path-amount-out
[path]
(if (= (:bridge-name path) constants/bridge-name-hop)
(let [{:keys [token-fees bonder-fees amount-in]} path]
(-> amount-in
money/from-hex
(money/sub token-fees)
(money/add bonder-fees)))
(-> path :amount-out money/from-hex)))
(defn- convert-wei-to-eth
[amount native-token? token-decimals]
(money/with-precision
(if native-token?
(money/wei->ether amount)
(money/token->unit amount token-decimals))
constants/token-display-precision))
(defn network-amounts-by-chain (defn network-amounts-by-chain
[{:keys [route token-decimals native-token? receiver?]}] [{:keys [route token-decimals native-token? receiver?]}]
(reduce (reduce
(fn [acc path] (fn [acc path]
(let [amount-hex (if receiver? (:amount-out path) (:amount-in path)) (let [amount (if receiver?
amount-units (native-module/hex-to-number (path-amount-out path)
(utils.hex/normalize-hex amount-hex)) (path-amount-in path))
amount (money/with-precision chain-id (if receiver?
(if native-token? (get-in path [:to :chain-id])
(money/wei->ether amount-units) (get-in path [:from :chain-id]))]
(money/token->unit amount-units (as-> amount $
token-decimals)) (convert-wei-to-eth $ native-token? token-decimals)
6) (update acc chain-id money/add $))))
chain-id (if receiver? (get-in path [:to :chain-id]) (get-in path [:from :chain-id]))] {}
(update acc chain-id money/add amount))) route))
(defn path-estimated-received
"Calculates the (`bignumber`) estimated received token amount. For
bridge transactions, the amount is the difference between the
`amount-in` and the `token-fees`."
[path]
(if (-> path :bridge-name (= constants/bridge-name-hop))
(-> path
:amount-in
money/from-hex
(money/sub (:token-fees path)))
(-> path
:amount-out
money/from-hex)))
(defn estimated-received-by-chain
[route token-decimals native-token?]
(reduce
(fn [acc path]
(let [chain-id (get-in path [:to :chain-id])
estimated-received (-> path
path-estimated-received
(convert-wei-to-eth native-token? token-decimals))]
(update acc chain-id money/add estimated-received)))
{} {}
route)) route))

View File

@ -79,9 +79,91 @@
:receiver? receiver?}) :receiver? receiver?})
expected {1 (money/bignumber "2") expected {1 (money/bignumber "2")
10 (money/bignumber "2")}] 10 (money/bignumber "2")}]
(doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value)))))
(testing "Correctly calculates network (out) amounts for bridge transaction"
(let [route [{:bridge-name "Hop"
:amount-in "0xde0b6b3a7640000"
:bonder-fees (money/bignumber "200000000000000")
:token-fees (money/bignumber "230000000000000")
:to {:chain-id 1}}
{:bridge-name "Hop"
:bonder-fees (money/bignumber "300000000000000")
:token-fees (money/bignumber "410000000000000")
:amount-in "0xde0b6b3a7640000"
:to {:chain-id 10}}]
token-decimals 6
native-token? true
receiver? true
result (utils/network-amounts-by-chain {:route route
:token-decimals token-decimals
:native-token? native-token?
:receiver? receiver?})
expected {1 (money/bignumber "0.99997")
10 (money/bignumber "0.99989")}]
(doseq [[chain-id exp-value] expected] (doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value)))))) (is (money/equal-to (get result chain-id) exp-value))))))
(deftest estimated-received-by-chain-test
(testing "Correctly calculates the bridge estimated received amount"
(let [chain-id 1
route [{:bridge-name "Hop"
:amount-in "0xde0b6b3a7640000"
:token-fees (money/bignumber "230000000000000")
:to {:chain-id 1}}]
token-decimals 18
native-token? true
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "0.99977")]
(is (money/equal-to (get result chain-id) expected))))
(testing "Correctly calculates the estimated received amount with native token"
(let [chain-id 1
route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}]
token-decimals 18
native-token? true
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "2")]
(is (money/equal-to (get result chain-id) expected))))
(testing "Correctly calculates the estimated received amount with non-native token"
(let [chain-id 10
route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}]
token-decimals 18
native-token? false
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "2")]
(is (money/equal-to (get result chain-id) expected))))
(testing
"Correctly calculates the estimated received amount with multiple routes on different networks"
(let [route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id 1}}
{:amount-out "0xde0b6b3a7640000"
:to {:chain-id 10}}]
token-decimals 18
native-token? false
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected {10 (money/bignumber "1")
1 (money/bignumber "2")}]
(doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value)))))
(testing "Correctly calculates the estimated received amount with multiple routes on the same network"
(let [chain-id 10
route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}
{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}]
token-decimals 18
native-token? false
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "4")]
(is (money/equal-to (get result chain-id) expected)))))
(deftest network-values-for-ui-test (deftest network-values-for-ui-test
(testing "Sanitizes values correctly for display" (testing "Sanitizes values correctly for display"
(let [amounts {1 (money/bignumber "0") (let [amounts {1 (money/bignumber "0")

View File

@ -1,7 +1,8 @@
(ns status-im.subs.wallet.networks (ns status-im.subs.wallet.networks
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.contexts.wallet.common.utils.networks :as network-utils] [status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.money :as money])) [utils.money :as money]
[utils.number :as number]))
(def max-network-prefixes 2) (def max-network-prefixes 2)
@ -62,28 +63,18 @@
(re-frame/reg-sub (re-frame/reg-sub
:wallet/network-values :wallet/network-values
:<- [:wallet/wallet-send] :<- [:wallet/wallet-send]
(fn [{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} [_ to-values?]] :<- [:wallet/send-display-token-decimals]
(fn [[{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} token-decimals]
[_ to-values?]]
(let [network-values (if to-values? to-values-by-chain from-values-by-chain) (let [network-values (if to-values? to-values-by-chain from-values-by-chain)
token-symbol (or token-display-name token-symbol (or token-display-name
(-> send-data :token :symbol))] (-> send-data :token :symbol))]
(reduce-kv (reduce-kv
(fn [acc chain-id amount] (fn [acc chain-id amount]
(let [network-name (network-utils/id->network chain-id)] (let [network-name (network-utils/id->network chain-id)
amount-fixed (number/to-fixed (money/->bignumber amount) token-decimals)]
(assoc acc (assoc acc
(if (= network-name :mainnet) :ethereum network-name) (if (= network-name :mainnet) :ethereum network-name)
{:amount amount :token-symbol token-symbol}))) {:amount amount-fixed :token-symbol token-symbol})))
{} {}
network-values)))) network-values))))
(re-frame/reg-sub
:wallet/total-amount
:<- [:wallet/wallet-send]
(fn [{:keys [from-values-by-chain to-values-by-chain]} [_ to-values?]]
(let [network-values (if to-values? to-values-by-chain from-values-by-chain)]
(reduce
(fn [acc amount]
(if (money/bignumber? amount)
(money/add acc amount)
acc))
(money/bignumber 0)
(vals network-values)))))

View File

@ -110,7 +110,7 @@
:to-values-by-chain {42161 100} :to-values-by-chain {42161 100}
:token-display-name "ETH"}) :token-display-name "ETH"})
(is (is
(match? {:ethereum {:amount 100 :token-symbol "ETH"}} (rf/sub [sub-name false])))) (match? {:ethereum {:amount "100" :token-symbol "ETH"}} (rf/sub [sub-name false]))))
(testing "network values for the to account are returned correctly" (testing "network values for the to account are returned correctly"
(swap! rf-db/app-db assoc-in (swap! rf-db/app-db assoc-in
@ -119,4 +119,4 @@
:to-values-by-chain {42161 100} :to-values-by-chain {42161 100}
:token-display-name "ARB1"}) :token-display-name "ARB1"})
(is (is
(match? {:arbitrum {:amount 100 :token-symbol "ARB1"}} (rf/sub [sub-name true]))))) (match? {:arbitrum {:amount "100" :token-symbol "ARB1"}} (rf/sub [sub-name true])))))

View File

@ -1,9 +1,12 @@
(ns status-im.subs.wallet.send (ns status-im.subs.wallet.send
(:require (:require
[re-frame.core :as rf] [re-frame.core :as rf]
[status-im.contexts.wallet.common.activity-tab.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.send.utils :as send-utils] [status-im.contexts.wallet.send.utils :as send-utils]
[utils.number])) [utils.money :as money]
[utils.number :as number]))
(rf/reg-sub (rf/reg-sub
:wallet/send-tab :wallet/send-tab
@ -21,6 +24,16 @@
:<- [:wallet/wallet-send] :<- [:wallet/wallet-send]
:-> :recipient) :-> :recipient)
(rf/reg-sub
:wallet/send-route
:<- [:wallet/wallet-send]
:-> :route)
(rf/reg-sub
:wallet/send-token
:<- [:wallet/wallet-send]
:-> :token)
(rf/reg-sub (rf/reg-sub
:wallet/send-transaction-ids :wallet/send-transaction-ids
:<- [:wallet/wallet-send] :<- [:wallet/wallet-send]
@ -64,7 +77,7 @@
(->> address-activity (->> address-activity
(sort :timestamp) (sort :timestamp)
(keep (fn [{:keys [activity-type recipient]}] (keep (fn [{:keys [activity-type recipient]}]
(when (= constants/wallet-activity-type-send activity-type) (when (= activity-constants/wallet-activity-type-send activity-type)
recipient))) recipient)))
(distinct))))) (distinct)))))
@ -91,3 +104,71 @@
(when (not= (:chain-id network) bridge-to-chain-id) (when (not= (:chain-id network) bridge-to-chain-id)
(:chain-id network))) (:chain-id network)))
networks))) networks)))
(rf/reg-sub
:wallet/send-token-decimals
:<- [:wallet/wallet-send]
(fn [{:keys [token collectible]}]
(if collectible 0 (:decimals token))))
(rf/reg-sub
:wallet/send-display-token-decimals
:<- [:wallet/wallet-send]
(fn [{:keys [token collectible]}]
(if collectible
0
(-> token
common-utils/token-usd-price
common-utils/one-cent-value
common-utils/calc-max-crypto-decimals
(min constants/min-token-decimals-to-display)))))
(rf/reg-sub
:wallet/send-native-token?
:<- [:wallet/wallet-send]
(fn [{:keys [token token-display-name]}]
(and token (= token-display-name "ETH"))))
(rf/reg-sub
:wallet/total-amount
:<- [:wallet/send-route]
:<- [:wallet/send-token-decimals]
:<- [:wallet/send-native-token?]
(fn [[route token-decimals native-token?]]
(let [default-amount (money/bignumber 0)]
(if route
(->> (send-utils/estimated-received-by-chain
route
token-decimals
native-token?)
vals
(reduce money/add default-amount))
default-amount))))
(rf/reg-sub
:wallet/send-total-amount-formatted
:<- [:wallet/total-amount]
:<- [:wallet/send-display-token-decimals]
:<- [:wallet/wallet-send-token-symbol]
(fn [[amount token-decimals token-symbol]]
(-> amount
(number/to-fixed token-decimals)
(str " " token-symbol))))
(rf/reg-sub
:wallet/send-amount-fixed
:<- [:wallet/send-display-token-decimals]
(fn [token-decimals [_ amount]]
(number/to-fixed (money/->bignumber amount) token-decimals)))
(rf/reg-sub
:wallet/bridge-to-network-details
:<- [:wallet/wallet-send]
:<- [:wallet/network-details]
(fn [[{:keys [bridge-to-chain-id]} networks]]
(when bridge-to-chain-id
(some (fn [network]
(when
(= (:chain-id network) bridge-to-chain-id)
network))
networks))))

View File

@ -6,8 +6,15 @@
[status-im.subs.root] [status-im.subs.root]
[status-im.subs.wallet.send] [status-im.subs.wallet.send]
[test-helpers.unit :as h] [test-helpers.unit :as h]
[utils.money :as money]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(def ^:private token-mock
{:decimals 8
:symbol "ETH"
:balances-per-chain {1 {:raw-balance "100"}}
:market-values-per-currency {:usd {:price 10000}}})
(h/deftest-sub :wallet/send-tab (h/deftest-sub :wallet/send-tab
[sub-name] [sub-name]
(testing "returns active tab for selecting address" (testing "returns active tab for selecting address"
@ -84,3 +91,144 @@
:timestamp 1588464000}}}) :timestamp 1588464000}}})
(assoc-in [:wallet :current-viewing-account-address] "acc1")))) (assoc-in [:wallet :current-viewing-account-address] "acc1"))))
(is (match? ["acc2" "acc4"] (rf/sub [sub-name]))))) (is (match? ["acc2" "acc4"] (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-token-decimals
[sub-name]
(testing "returns the decimals for the chosen token"
(swap! rf-db/app-db assoc-in [:wallet :ui :send :token] token-mock)
(is (= 8 (rf/sub [sub-name]))))
(testing "returns 0 if sending collectibles"
(swap! rf-db/app-db assoc-in [:wallet :ui :send :collectible] {})
(is (match? 0 (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-display-token-decimals
[sub-name]
(testing "returns the decimals based on the token price for the chosen token"
(swap! rf-db/app-db assoc-in
[:wallet :ui :send :token]
{:symbol "ETH"
:balances-per-chain {1 {:raw-balance "100"}}
:market-values-per-currency {:usd {:price 10000}}})
(is (match? 6 (rf/sub [sub-name]))))
(testing "returns 0 if sending collectibles"
(swap! rf-db/app-db assoc-in [:wallet :ui :send :collectible] {})
(is (match? 0 (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-native-token?
[sub-name]
(testing "returns true if the chosen token is native (ETH)"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] {})
(assoc-in [:wallet :ui :send :token-display-name] "ETH"))))
(is (rf/sub [sub-name])))
(testing "returns false if the chosen token is not native"
(swap! rf-db/app-db
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] {})
(assoc-in [:wallet :ui :send :token-display-name] "SNT")))))
(is (not (rf/sub [sub-name])))))
(h/deftest-sub :wallet/total-amount
[sub-name]
(testing "returns the total SEND amount when the route is present"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH")
(assoc-in [:wallet :ui :send :route]
[{:amount-out "0x1bc16d674ec80000"
:to {:chain-id 1}}]))))
(is (match? (money/bignumber 2) (rf/sub [sub-name]))))
(testing "returns the total BRIDGE amount when the route is present"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH")
(assoc-in [:wallet :ui :send :route]
[{:bridge-name "Hop"
:amount-in "0xde0b6b3a7640000"
:token-fees (money/bignumber "230000000000000")
:to {:chain-id 1}}]))))
(is (match? (money/bignumber 0.99977) (rf/sub [sub-name]))))
(testing "returns the default total amount (0) when the route is not present"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH"))))
(is (match? (money/bignumber 0) (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-total-amount-formatted
[sub-name]
(testing "returns the formatted total amount"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH")
(assoc-in [:wallet :ui :send :route]
[{:amount-out "0x2b139f68a611c00" ;; 193999990000000000
:to {:chain-id 1}}]))))
(is (match? "0.194 ETH" (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-amount-fixed
[sub-name]
(testing "returns the fixed value when the amount is a string"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock))))
(is (match? "2" (rf/sub [sub-name "1.9999999"]))))
(testing "returns the fixed value when the amount is a number"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock))))
(is (match? "2" (rf/sub [sub-name 1.9999999]))))
(testing "returns the fixed value when the amount is a bignumber"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock))))
(is (match? "2" (rf/sub [sub-name (money/bignumber "1.9999999")])))))
(def ^:private mainnet-network
{:short-name "eth"
:network-name :mainnet
:abbreviated-name "Eth."
:full-name "Mainnet"
:chain-id 1
:related-chain-id 1
:layer 1})
(h/deftest-sub :wallet/bridge-to-network-details
[sub-name]
(testing "returns the network details for bridge transactions"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :networks] {:prod [mainnet-network]})
(assoc-in [:profile/profile :test-networks-enabled?] false)
(assoc-in [:wallet :ui :send] {:bridge-to-chain-id 1}))))
(is (match? mainnet-network (rf/sub [sub-name]))))
(testing "returns nil if not on the bridge flow"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :networks] {:prod [mainnet-network]})
(assoc-in [:profile/profile :test-networks-enabled?] false))))
(is (match? nil (rf/sub [sub-name])))))

View File

@ -154,7 +154,8 @@
(rf/reg-sub (rf/reg-sub
:wallet/wallet-send-token-symbol :wallet/wallet-send-token-symbol
:<- [:wallet/wallet-send] :<- [:wallet/wallet-send]
:-> :token-symbol) (fn [{:keys [token-symbol token]}]
(or token-symbol (:symbol token))))
(rf/reg-sub (rf/reg-sub
:wallet/wallet-send-disabled-from-chain-ids :wallet/wallet-send-disabled-from-chain-ids

View File

@ -2637,6 +2637,8 @@
"unblock": "Unblock", "unblock": "Unblock",
"unblock-contact": "Unblock this user", "unblock-contact": "Unblock this user",
"unblocking-a-user-message": "After unblocking {{username}}, you will be able to connect with them as a contact and see their messages in group chats and communities.", "unblocking-a-user-message": "After unblocking {{username}}, you will be able to connect with them as a contact and see their messages in group chats and communities.",
"understanding-bonder-fees-description": "The bridged amount can be lower than the amount you send due to Bonder fees. These fees cover the cost of providing liquidity and managing risks associated with your transfer. The fee ranges from 0.05% to 0.30%, depending on factors such as transaction volume and route specifics.",
"understanding-bonder-fees-title": "Understanding Bonder fees",
"undo": "Undo", "undo": "Undo",
"unique-identifiers": "Unique profile identifiers", "unique-identifiers": "Unique profile identifiers",
"universally-unique-identifiers-of-device": "Universally Unique Identifiers of device", "universally-unique-identifiers-of-device": "Universally Unique Identifiers of device",