refactor: suggested routes rendering (#19768)
Signed-off-by: Brian Sztamfater <brian@status.im>
This commit is contained in:
parent
d8d1e030e4
commit
355e144ef7
|
@ -30,7 +30,7 @@
|
||||||
[{:keys [network status amount container-style on-press] :as args}]
|
[{:keys [network status amount container-style on-press] :as args}]
|
||||||
(let [theme (quo.theme/use-theme)]
|
(let [theme (quo.theme/use-theme)]
|
||||||
(if (= status :add)
|
(if (= status :add)
|
||||||
[network-bridge-add args]
|
[network-bridge-add (assoc args :theme theme)]
|
||||||
[rn/pressable
|
[rn/pressable
|
||||||
{:style (merge (style/container network status theme) container-style)
|
{:style (merge (style/container network status theme) container-style)
|
||||||
:accessible true
|
:accessible true
|
||||||
|
|
|
@ -25,10 +25,10 @@
|
||||||
|
|
||||||
(def link-1x-container
|
(def link-1x-container
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:height 58
|
:height 57
|
||||||
:justify-content :center})
|
:justify-content :center})
|
||||||
|
|
||||||
(def link-2x-container
|
(def link-2x-container
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:height 114
|
:height 112
|
||||||
:justify-content :center})
|
:justify-content :center})
|
||||||
|
|
|
@ -24,33 +24,50 @@
|
||||||
:strokeWidth "1"}]])
|
:strokeWidth "1"}]])
|
||||||
|
|
||||||
(defn- line
|
(defn- line
|
||||||
[stroke width]
|
[{:keys [stroke-color source-color destination-color width theme]}]
|
||||||
[svg/svg
|
[svg/svg
|
||||||
{:height "10"
|
{:height "10"
|
||||||
:width "100%"
|
:width "100%"
|
||||||
:view-box (str "0 0 " width " 10")}
|
:view-box (str "0 0 " width " 10")}
|
||||||
[svg/path
|
[svg/path
|
||||||
{:d (str "M0,5 L" width ",5")
|
{:d (str "M0,5 L" width ",5")
|
||||||
:stroke stroke
|
:stroke stroke-color
|
||||||
:stroke-width "1"}]])
|
:stroke-width "1"}]
|
||||||
|
[svg/defs
|
||||||
|
[svg/linear-gradient
|
||||||
|
{:id "gradient"
|
||||||
|
:x1 "0%"
|
||||||
|
:x2 "100%"
|
||||||
|
:y1 "0%"
|
||||||
|
:y2 "0%"
|
||||||
|
:gradient-units "objectBoundingBox"}
|
||||||
|
[svg/stop {:offset "0%" :stop-color (colors/resolve-color source-color theme)}]
|
||||||
|
[svg/stop {:offset "100%" :stop-color (colors/resolve-color destination-color theme)}]]]])
|
||||||
|
|
||||||
(defn link-linear
|
(defn link-linear
|
||||||
[{:keys [source]}]
|
[{:keys [source destination]}]
|
||||||
(let [theme (quo.theme/use-theme)
|
(let [theme (quo.theme/use-theme)
|
||||||
[container-width
|
[container-width
|
||||||
set-container-width] (rn/use-state 100)
|
set-container-width] (rn/use-state 100)
|
||||||
stroke-color (colors/resolve-color source theme)
|
stroke-color "url(#gradient)"
|
||||||
|
source-color (colors/resolve-color source theme)
|
||||||
|
destination-color (colors/resolve-color destination theme)
|
||||||
fill-color (colors/theme-colors colors/white colors/neutral-90 theme)
|
fill-color (colors/theme-colors colors/white colors/neutral-90 theme)
|
||||||
on-layout (rn/use-callback #(set-container-width
|
on-layout (rn/use-callback #(set-container-width
|
||||||
(oget % :nativeEvent :layout :width)))]
|
(oget % :nativeEvent :layout :width)))]
|
||||||
[rn/view
|
[rn/view
|
||||||
{:style style/link-linear-container
|
{:style style/link-linear-container
|
||||||
:on-layout on-layout}
|
:on-layout on-layout}
|
||||||
[line stroke-color container-width]
|
[line
|
||||||
|
{:stroke-color stroke-color
|
||||||
|
:source-color source-color
|
||||||
|
:destination-color destination-color
|
||||||
|
:width container-width
|
||||||
|
:theme theme}]
|
||||||
[rn/view {:style style/left-circle-container}
|
[rn/view {:style style/left-circle-container}
|
||||||
[circle fill-color stroke-color]]
|
[circle fill-color source-color]]
|
||||||
[rn/view {:style style/right-circle-container}
|
[rn/view {:style style/right-circle-container}
|
||||||
[circle fill-color stroke-color]]]))
|
[circle fill-color destination-color]]]))
|
||||||
|
|
||||||
(defn link-1x
|
(defn link-1x
|
||||||
[{:keys [source destination]}]
|
[{:keys [source destination]}]
|
||||||
|
|
|
@ -263,3 +263,11 @@
|
||||||
{}
|
{}
|
||||||
tokens)
|
tokens)
|
||||||
(update-vals #(prettify-balance currency-symbol %))))
|
(update-vals #(prettify-balance currency-symbol %))))
|
||||||
|
|
||||||
|
(defn format-token-id
|
||||||
|
[token collectible]
|
||||||
|
(if token
|
||||||
|
(:symbol token)
|
||||||
|
(str (get-in collectible [:id :contract-id :address])
|
||||||
|
":"
|
||||||
|
(get-in collectible [:id :token-id]))))
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
(ns status-im.contexts.wallet.common.utils.send
|
|
||||||
(:require [clojure.string :as string]
|
|
||||||
[utils.money :as money]))
|
|
||||||
|
|
||||||
(defn calculate-gas-fee
|
|
||||||
[data]
|
|
||||||
(let [gas-amount (money/bignumber (get data :gas-amount))
|
|
||||||
gas-fees (get data :gas-fees)
|
|
||||||
eip1559-enabled? (get gas-fees :eip-1559-enabled)
|
|
||||||
optimal-price-gwei (money/bignumber (if eip1559-enabled?
|
|
||||||
(get gas-fees :max-fee-per-gas-medium)
|
|
||||||
(get gas-fees :gas-price)))
|
|
||||||
total-gas-fee-wei (money/mul (money/->wei :gwei optimal-price-gwei) gas-amount)
|
|
||||||
l1-fee-wei (money/->wei :gwei (get gas-fees :l-1-gas-fee))]
|
|
||||||
(money/add total-gas-fee-wei l1-fee-wei)))
|
|
||||||
|
|
||||||
(defn calculate-full-route-gas-fee
|
|
||||||
"Sums all the routes fees in wei and then convert the total value to ether"
|
|
||||||
[route]
|
|
||||||
(money/wei->ether (reduce money/add (map calculate-gas-fee route))))
|
|
||||||
|
|
||||||
(defn find-affordable-networks
|
|
||||||
[{:keys [balances-per-chain input-value selected-networks disabled-chain-ids]}]
|
|
||||||
(let [input-value (if (string/blank? input-value) 0 input-value)]
|
|
||||||
(->> balances-per-chain
|
|
||||||
(filter (fn [[_
|
|
||||||
{:keys [balance chain-id]
|
|
||||||
:or {balance 0}}]]
|
|
||||||
(and
|
|
||||||
(money/greater-than-or-equals (money/bignumber balance)
|
|
||||||
(money/bignumber input-value))
|
|
||||||
(some #(= % chain-id) selected-networks)
|
|
||||||
(not-any? #(= % chain-id) disabled-chain-ids))))
|
|
||||||
(map first))))
|
|
|
@ -1,116 +0,0 @@
|
||||||
(ns status-im.contexts.wallet.common.utils.send-test
|
|
||||||
(:require [cljs.test :refer [deftest is testing]]
|
|
||||||
[status-im.contexts.wallet.common.utils.send :as utils]
|
|
||||||
[utils.money :as money]))
|
|
||||||
|
|
||||||
(deftest test-calculate-gas-fee
|
|
||||||
(testing "EIP-1559 transaction without L1 fee"
|
|
||||||
(let [data {:gas-amount "23487"
|
|
||||||
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
|
||||||
:eip-1559-enabled true
|
|
||||||
:l-1-gas-fee "0"}}
|
|
||||||
expected-result (money/bignumber "53063589834657")] ; This is in Wei
|
|
||||||
(is (money/equal-to (utils/calculate-gas-fee data)
|
|
||||||
expected-result))))
|
|
||||||
|
|
||||||
(testing "EIP-1559 transaction with L1 fee of 60,000 Gwei"
|
|
||||||
(let [data {:gas-amount "23487"
|
|
||||||
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
|
||||||
:eip-1559-enabled true
|
|
||||||
:l-1-gas-fee "60000"}}
|
|
||||||
expected-result (money/bignumber "113063589834657")] ; Added 60,000 Gwei in Wei to the
|
|
||||||
; previous result
|
|
||||||
(is (money/equal-to (utils/calculate-gas-fee data)
|
|
||||||
expected-result))))
|
|
||||||
|
|
||||||
(testing "Non-EIP-1559 transaction with specified gas price"
|
|
||||||
(let [data {:gas-amount "23487"
|
|
||||||
:gas-fees {:gas-price "2.872721089"
|
|
||||||
:eip-1559-enabled false
|
|
||||||
:l-1-gas-fee "0"}}
|
|
||||||
expected-result (money/bignumber "67471600217343")] ; This is in Wei, for the specified
|
|
||||||
; gas amount and price
|
|
||||||
(is (money/equal-to (utils/calculate-gas-fee data)
|
|
||||||
expected-result)))))
|
|
||||||
|
|
||||||
(deftest test-calculate-full-route-gas-fee
|
|
||||||
(testing "Route with a single EIP-1559 transaction, no L1 fees"
|
|
||||||
(let [route [{:gas-amount "23487"
|
|
||||||
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
|
||||||
:eip-1559-enabled true
|
|
||||||
:l-1-gas-fee "0"}}]
|
|
||||||
expected-result (money/bignumber "0.000053063589834657")] ; The Wei amount for the
|
|
||||||
; transaction, converted to
|
|
||||||
; Ether
|
|
||||||
(is (money/equal-to (utils/calculate-full-route-gas-fee route)
|
|
||||||
expected-result))))
|
|
||||||
|
|
||||||
(testing "Route with two EIP-1559 transactions, no L1 fees"
|
|
||||||
(let [route [{:gas-amount "23487"
|
|
||||||
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
|
||||||
:eip-1559-enabled true
|
|
||||||
:l-1-gas-fee "0"}}
|
|
||||||
{:gas-amount "23487"
|
|
||||||
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
|
||||||
:eip-1559-enabled true
|
|
||||||
:l-1-gas-fee "0"}}]
|
|
||||||
expected-result (money/bignumber "0.000106127179669314")] ; Sum of both transactions' Wei
|
|
||||||
; amounts, converted to Ether
|
|
||||||
(is (money/equal-to (utils/calculate-full-route-gas-fee route)
|
|
||||||
expected-result))))
|
|
||||||
|
|
||||||
(testing "Route with two EIP-1559 transactions, one with L1 fee of 60,000 Gwei"
|
|
||||||
(let [route [{:gas-amount "23487"
|
|
||||||
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
|
||||||
:eip-1559-enabled true
|
|
||||||
:l-1-gas-fee "0"}}
|
|
||||||
{:gas-amount "23487"
|
|
||||||
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
|
||||||
:eip-1559-enabled true
|
|
||||||
:l-1-gas-fee "60000"}}]
|
|
||||||
expected-result (money/bignumber "0.000166127179669314")] ; Added 60,000 Gwei in Wei to
|
|
||||||
; the previous total and
|
|
||||||
; converted to Ether
|
|
||||||
(is (money/equal-to (utils/calculate-full-route-gas-fee route)
|
|
||||||
expected-result)))))
|
|
||||||
|
|
||||||
(deftest test-find-affordable-networks
|
|
||||||
(testing "All networks affordable and selected, none disabled"
|
|
||||||
(let [balances-per-chain {"1" {:balance "50.0" :chain-id "1"}
|
|
||||||
"2" {:balance "40.0" :chain-id "2"}}
|
|
||||||
input-value 20
|
|
||||||
selected-networks ["1" "2"]
|
|
||||||
disabled-chain-ids []
|
|
||||||
expected ["1" "2"]]
|
|
||||||
(is (= (set (utils/find-affordable-networks {:balances-per-chain balances-per-chain
|
|
||||||
:input-value input-value
|
|
||||||
:selected-networks selected-networks
|
|
||||||
:disabled-chain-ids disabled-chain-ids}))
|
|
||||||
(set expected)))))
|
|
||||||
|
|
||||||
(testing "No networks affordable"
|
|
||||||
(let [balances-per-chain {"1" {:balance "5.0" :chain-id "1"}
|
|
||||||
"2" {:balance "1.0" :chain-id "2"}}
|
|
||||||
input-value 10
|
|
||||||
selected-networks ["1" "2"]
|
|
||||||
disabled-chain-ids []
|
|
||||||
expected []]
|
|
||||||
(is (= (set (utils/find-affordable-networks {:balances-per-chain balances-per-chain
|
|
||||||
:input-value input-value
|
|
||||||
:selected-networks selected-networks
|
|
||||||
:disabled-chain-ids disabled-chain-ids}))
|
|
||||||
(set expected)))))
|
|
||||||
|
|
||||||
(testing "Selected networks subset, with some disabled"
|
|
||||||
(let [balances-per-chain {"1" {:balance "100.0" :chain-id "1"}
|
|
||||||
"2" {:balance "50.0" :chain-id "2"}
|
|
||||||
"3" {:balance "20.0" :chain-id "3"}}
|
|
||||||
input-value 15
|
|
||||||
selected-networks ["1" "2" "3"]
|
|
||||||
disabled-chain-ids ["2"]
|
|
||||||
expected ["1" "3"]]
|
|
||||||
(is (= (set (utils/find-affordable-networks {:balances-per-chain balances-per-chain
|
|
||||||
:input-value input-value
|
|
||||||
:selected-networks selected-networks
|
|
||||||
:disabled-chain-ids disabled-chain-ids}))
|
|
||||||
(set expected))))))
|
|
|
@ -31,8 +31,13 @@
|
||||||
token (get-in db [:wallet :ui :send :token])
|
token (get-in db [:wallet :ui :send :token])
|
||||||
collectible (get-in db [:wallet :ui :send :collectible])
|
collectible (get-in db [:wallet :ui :send :collectible])
|
||||||
token-display-name (get-in db [:wallet :ui :send :token-display-name])
|
token-display-name (get-in db [:wallet :ui :send :token-display-name])
|
||||||
|
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
|
||||||
|
receiver-network-values (get-in db [:wallet :ui :send :receiver-network-values])
|
||||||
|
sender-network-values (get-in db [:wallet :ui :send :sender-network-values])
|
||||||
|
disabled-from-chain-ids (or (get-in db [:wallet :ui :send :disabled-from-chain-ids]) [])
|
||||||
token-decimals (if collectible 0 (:decimals token))
|
token-decimals (if collectible 0 (:decimals token))
|
||||||
native-token? (and token (= token-display-name "ETH"))
|
native-token? (and token (= token-display-name "ETH"))
|
||||||
|
routes-available? (pos? (count chosen-route))
|
||||||
from-network-amounts-by-chain (send-utils/network-amounts-by-chain {:route chosen-route
|
from-network-amounts-by-chain (send-utils/network-amounts-by-chain {:route chosen-route
|
||||||
:token-decimals
|
:token-decimals
|
||||||
token-decimals
|
token-decimals
|
||||||
|
@ -46,19 +51,54 @@
|
||||||
:native-token?
|
:native-token?
|
||||||
native-token?
|
native-token?
|
||||||
:to? true})
|
:to? true})
|
||||||
to-network-values-for-ui (send-utils/network-values-for-ui to-network-amounts-by-chain)]
|
to-network-values-for-ui (send-utils/network-values-for-ui to-network-amounts-by-chain)
|
||||||
|
sender-network-values (if routes-available?
|
||||||
|
(send-utils/network-amounts from-network-values-for-ui
|
||||||
|
disabled-from-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
false)
|
||||||
|
(send-utils/reset-network-amounts-to-zero
|
||||||
|
sender-network-values))
|
||||||
|
receiver-network-values (if routes-available?
|
||||||
|
(send-utils/network-amounts to-network-values-for-ui
|
||||||
|
disabled-from-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
true)
|
||||||
|
(send-utils/reset-network-amounts-to-zero
|
||||||
|
receiver-network-values))
|
||||||
|
network-links (when routes-available?
|
||||||
|
(send-utils/network-links chosen-route
|
||||||
|
sender-network-values
|
||||||
|
receiver-network-values))]
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(assoc-in [:wallet :ui :send :suggested-routes] suggested-routes-data)
|
(assoc-in [:wallet :ui :send :suggested-routes] suggested-routes-data)
|
||||||
(assoc-in [:wallet :ui :send :route] chosen-route)
|
(assoc-in [:wallet :ui :send :route] chosen-route)
|
||||||
(assoc-in [:wallet :ui :send :from-values-by-chain] from-network-values-for-ui)
|
(assoc-in [:wallet :ui :send :from-values-by-chain] from-network-values-for-ui)
|
||||||
(assoc-in [:wallet :ui :send :to-values-by-chain] to-network-values-for-ui)
|
(assoc-in [:wallet :ui :send :to-values-by-chain] to-network-values-for-ui)
|
||||||
|
(assoc-in [:wallet :ui :send :sender-network-values] sender-network-values)
|
||||||
|
(assoc-in [:wallet :ui :send :receiver-network-values] receiver-network-values)
|
||||||
|
(assoc-in [:wallet :ui :send :network-links] network-links)
|
||||||
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false))}))))
|
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false))}))))
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet/suggested-routes-error
|
(rf/reg-event-fx :wallet/suggested-routes-error
|
||||||
(fn [{:keys [db]} [_error]]
|
(fn [{:keys [db]} [error]]
|
||||||
{:db (-> db
|
(let [cleaned-sender-network-values (-> (get-in db [:wallet :ui :send :sender-network-values])
|
||||||
(update-in [:wallet :ui :send] dissoc :suggested-routes :route)
|
(send-utils/reset-network-amounts-to-zero))
|
||||||
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false))}))
|
cleaned-receiver-network-values (-> (get-in db [:wallet :ui :send :receiver-network-values])
|
||||||
|
(send-utils/reset-network-amounts-to-zero))]
|
||||||
|
{:db (-> db
|
||||||
|
(update-in [:wallet :ui :send]
|
||||||
|
dissoc
|
||||||
|
:route)
|
||||||
|
(assoc-in [:wallet :ui :send :sender-network-values] cleaned-sender-network-values)
|
||||||
|
(assoc-in [:wallet :ui :send :receiver-network-values] cleaned-receiver-network-values)
|
||||||
|
(assoc-in [:wallet :ui :send :loading-suggested-routes?] false)
|
||||||
|
(assoc-in [:wallet :ui :send :suggested-routes] {:best []}))
|
||||||
|
:fx [[:dispatch
|
||||||
|
[:toasts/upsert
|
||||||
|
{:id :send-transaction-error
|
||||||
|
:type :negative
|
||||||
|
:text (:message error)}]]]})))
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet/clean-suggested-routes
|
(rf/reg-event-fx :wallet/clean-suggested-routes
|
||||||
(fn [{:keys [db]}]
|
(fn [{:keys [db]}]
|
||||||
|
@ -69,6 +109,9 @@
|
||||||
:route
|
:route
|
||||||
:from-values-by-chain
|
:from-values-by-chain
|
||||||
:to-values-by-chain
|
:to-values-by-chain
|
||||||
|
:sender-network-values
|
||||||
|
:receiver-network-values
|
||||||
|
:network-links
|
||||||
:loading-suggested-routes?
|
:loading-suggested-routes?
|
||||||
:suggested-routes-call-timestamp)}))
|
:suggested-routes-call-timestamp)}))
|
||||||
|
|
||||||
|
@ -203,56 +246,74 @@
|
||||||
|
|
||||||
(rf/reg-event-fx :wallet/get-suggested-routes
|
(rf/reg-event-fx :wallet/get-suggested-routes
|
||||||
(fn [{:keys [db now]} [{:keys [amount]}]]
|
(fn [{:keys [db now]} [{:keys [amount]}]]
|
||||||
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
|
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
|
||||||
token (get-in db [:wallet :ui :send :token])
|
token (get-in db [:wallet :ui :send :token])
|
||||||
transaction-type (get-in db [:wallet :ui :send :tx-type])
|
transaction-type (get-in db [:wallet :ui :send :tx-type])
|
||||||
collectible (get-in db [:wallet :ui :send :collectible])
|
collectible (get-in db [:wallet :ui :send :collectible])
|
||||||
to-address (get-in db [:wallet :ui :send :to-address])
|
to-address (get-in db [:wallet :ui :send :to-address])
|
||||||
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
|
receiver-networks (get-in db [:wallet :ui :send :receiver-networks])
|
||||||
disabled-from-chain-ids (or (get-in db [:wallet :ui :send :disabled-from-chain-ids]) [])
|
disabled-from-chain-ids (or (get-in db [:wallet :ui :send :disabled-from-chain-ids]) [])
|
||||||
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
|
||||||
networks ((if test-networks-enabled? :test :prod)
|
networks ((if test-networks-enabled? :test :prod)
|
||||||
(get-in db [:wallet :networks]))
|
(get-in db [:wallet :networks]))
|
||||||
network-chain-ids (map :chain-id networks)
|
network-chain-ids (map :chain-id networks)
|
||||||
bridge-to-chain-id (get-in db [:wallet :ui :send :bridge-to-chain-id])
|
bridge-to-chain-id (get-in db [:wallet :ui :send :bridge-to-chain-id])
|
||||||
token-decimal (when token (:decimals token))
|
token-decimal (when token (:decimals token))
|
||||||
token-id (if token
|
token-id (utils/format-token-id token collectible)
|
||||||
(:symbol token)
|
to-token-id ""
|
||||||
(str (get-in collectible [:id :contract-id :address])
|
network-preferences (if token [] [(get-in collectible [:id :contract-id :chain-id])])
|
||||||
":"
|
gas-rates constants/gas-rate-medium
|
||||||
(get-in collectible [:id :token-id])))
|
amount-in (send-utils/amount-in-hex amount (if token token-decimal 0))
|
||||||
to-token-id ""
|
from-address wallet-address
|
||||||
network-preferences (if token [] [(get-in collectible [:id :contract-id :chain-id])])
|
|
||||||
gas-rates constants/gas-rate-medium
|
|
||||||
amount-in (send-utils/amount-in-hex amount (if token token-decimal 0))
|
|
||||||
from-address wallet-address
|
|
||||||
disabled-from-chain-ids disabled-from-chain-ids
|
disabled-from-chain-ids disabled-from-chain-ids
|
||||||
disabled-to-chain-ids (if (= transaction-type :bridge)
|
disabled-to-chain-ids (if (= transaction-type :bridge)
|
||||||
(filter #(not= % bridge-to-chain-id) network-chain-ids)
|
(filter #(not= % bridge-to-chain-id) network-chain-ids)
|
||||||
(filter (fn [chain-id]
|
(filter (fn [chain-id]
|
||||||
(not (some #(= chain-id %)
|
(not (some #(= chain-id %)
|
||||||
receiver-networks)))
|
receiver-networks)))
|
||||||
network-chain-ids))
|
network-chain-ids))
|
||||||
from-locked-amount {}
|
from-locked-amount {}
|
||||||
transaction-type-param (case transaction-type
|
transaction-type-param (case transaction-type
|
||||||
:collectible constants/send-type-erc-721-transfer
|
:collectible constants/send-type-erc-721-transfer
|
||||||
:bridge constants/send-type-bridge
|
:bridge constants/send-type-bridge
|
||||||
constants/send-type-transfer)
|
constants/send-type-transfer)
|
||||||
request-params [transaction-type-param
|
balances-per-chain (when token (:balances-per-chain token))
|
||||||
from-address
|
token-available-networks-for-suggested-routes
|
||||||
to-address
|
(when token
|
||||||
amount-in
|
(send-utils/token-available-networks-for-suggested-routes {:balances-per-chain
|
||||||
token-id
|
balances-per-chain
|
||||||
to-token-id
|
:disabled-chain-ids
|
||||||
|
disabled-from-chain-ids}))
|
||||||
|
sender-network-values (when token-available-networks-for-suggested-routes
|
||||||
|
(send-utils/loading-network-amounts
|
||||||
|
token-available-networks-for-suggested-routes
|
||||||
disabled-from-chain-ids
|
disabled-from-chain-ids
|
||||||
disabled-to-chain-ids
|
receiver-networks
|
||||||
network-preferences
|
false))
|
||||||
gas-rates
|
receiver-network-values (when token-available-networks-for-suggested-routes
|
||||||
from-locked-amount]]
|
(send-utils/loading-network-amounts
|
||||||
|
token-available-networks-for-suggested-routes
|
||||||
|
disabled-from-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
true))
|
||||||
|
request-params [transaction-type-param
|
||||||
|
from-address
|
||||||
|
to-address
|
||||||
|
amount-in
|
||||||
|
token-id
|
||||||
|
to-token-id
|
||||||
|
disabled-from-chain-ids
|
||||||
|
disabled-to-chain-ids
|
||||||
|
network-preferences
|
||||||
|
gas-rates
|
||||||
|
from-locked-amount]]
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(assoc-in [:wallet :ui :send :amount] amount)
|
(assoc-in [:wallet :ui :send :amount] amount)
|
||||||
(assoc-in [:wallet :ui :send :loading-suggested-routes?] true)
|
(assoc-in [:wallet :ui :send :loading-suggested-routes?] true)
|
||||||
(assoc-in [:wallet :ui :send :suggested-routes-call-timestamp] now))
|
(assoc-in [:wallet :ui :send :sender-network-values] sender-network-values)
|
||||||
|
(assoc-in [:wallet :ui :send :receiver-network-values] receiver-network-values)
|
||||||
|
(assoc-in [:wallet :ui :send :suggested-routes-call-timestamp] now)
|
||||||
|
(update-in [:wallet :ui :send] dissoc :network-links))
|
||||||
:json-rpc/call [{:method "wallet_getSuggestedRoutes"
|
:json-rpc/call [{:method "wallet_getSuggestedRoutes"
|
||||||
:params request-params
|
:params request-params
|
||||||
:on-success (fn [suggested-routes]
|
:on-success (fn [suggested-routes]
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
:gas-fees {:base-fee "32.325296406"
|
:gas-fees {:base-fee "32.325296406"
|
||||||
:max-priority-fee-per-gas "0.011000001"
|
:max-priority-fee-per-gas "0.011000001"
|
||||||
:eip1559-enabled true}}]
|
:eip1559-enabled true}}]
|
||||||
:wallet/wallet-send-suggested-routes {:candidates []}
|
:wallet/wallet-send-suggested-routes nil
|
||||||
:wallet/wallet-send-receiver-networks [1]
|
:wallet/wallet-send-receiver-networks [1]
|
||||||
:view-id :screen/wallet.send-input-amount
|
:view-id :screen/wallet.send-input-amount
|
||||||
:wallet/wallet-send-to-address "0x04371e2d9d66b82f056bc128064"
|
:wallet/wallet-send-to-address "0x04371e2d9d66b82f056bc128064"
|
||||||
|
@ -73,7 +73,10 @@
|
||||||
:market-values-per-currency {:usd {:price 10}}}
|
:market-values-per-currency {:usd {:price 10}}}
|
||||||
:wallet/wallet-send-disabled-from-chain-ids []
|
:wallet/wallet-send-disabled-from-chain-ids []
|
||||||
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
|
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
|
||||||
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}})
|
:wallet/wallet-send-to-values-by-chain {1 (money/bignumber "250")}
|
||||||
|
:wallet/wallet-send-sender-network-values nil
|
||||||
|
:wallet/wallet-send-receiver-network-values nil
|
||||||
|
:wallet/wallet-send-network-links nil})
|
||||||
|
|
||||||
(h/describe "Send > input amount screen"
|
(h/describe "Send > input amount screen"
|
||||||
(h/setup-restorable-re-frame)
|
(h/setup-restorable-re-frame)
|
||||||
|
|
|
@ -33,3 +33,8 @@
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:height 40
|
:height 40
|
||||||
:background-color :transparent})
|
:background-color :transparent})
|
||||||
|
|
||||||
|
(def no-routes-found-container
|
||||||
|
{:height 40
|
||||||
|
:width "100%"
|
||||||
|
:align-items :center})
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
[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.common.utils.send :as send-utils]
|
|
||||||
[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.send.utils :as send-utils]
|
||||||
[utils.address :as address]
|
[utils.address :as address]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
|
[utils.money :as money]
|
||||||
[utils.re-frame :as rf]))
|
[utils.re-frame :as rf]))
|
||||||
|
|
||||||
(defn- make-limit-label
|
(defn- make-limit-label
|
||||||
|
@ -52,6 +53,15 @@
|
||||||
:title (i18n/label :t/user-gets {:name receiver})
|
:title (i18n/label :t/user-gets {:name receiver})
|
||||||
:subtitle amount}]])
|
:subtitle amount}]])
|
||||||
|
|
||||||
|
(defn- every-network-value-is-zero?
|
||||||
|
[sender-network-values]
|
||||||
|
(every? (fn [{:keys [total-amount]}]
|
||||||
|
(and
|
||||||
|
total-amount
|
||||||
|
(money/equal-to total-amount
|
||||||
|
(money/bignumber "0"))))
|
||||||
|
sender-network-values))
|
||||||
|
|
||||||
(defn select-asset-bottom-sheet
|
(defn select-asset-bottom-sheet
|
||||||
[clear-input!]
|
[clear-input!]
|
||||||
(let [{preselected-token-symbol :symbol} (rf/sub [:wallet/wallet-send-token])]
|
(let [{preselected-token-symbol :symbol} (rf/sub [:wallet/wallet-send-token])]
|
||||||
|
@ -157,7 +167,18 @@
|
||||||
[:show-bottom-sheet
|
[:show-bottom-sheet
|
||||||
{:content (fn []
|
{:content (fn []
|
||||||
[select-asset-bottom-sheet
|
[select-asset-bottom-sheet
|
||||||
clear-input!])}])]
|
clear-input!])}])
|
||||||
|
loading-suggested-routes? (rf/sub
|
||||||
|
[:wallet/wallet-send-loading-suggested-routes?])
|
||||||
|
sender-network-values (rf/sub
|
||||||
|
[:wallet/wallet-send-sender-network-values])
|
||||||
|
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
|
||||||
|
routes (when suggested-routes
|
||||||
|
(or (:best suggested-routes) []))
|
||||||
|
no-routes-found? (and
|
||||||
|
(every-network-value-is-zero? sender-network-values)
|
||||||
|
(not (nil? routes))
|
||||||
|
(not loading-suggested-routes?))]
|
||||||
(rn/use-mount
|
(rn/use-mount
|
||||||
(fn []
|
(fn []
|
||||||
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
|
(let [dismiss-keyboard-fn #(when (= % "active") (rn/dismiss-keyboard!))
|
||||||
|
@ -200,12 +221,28 @@
|
||||||
:fees fee-formatted
|
:fees fee-formatted
|
||||||
:amount amount-text
|
:amount amount-text
|
||||||
:receiver (address/get-shortened-key to-address)}])
|
:receiver (address/get-shortened-key to-address)}])
|
||||||
|
(when no-routes-found?
|
||||||
|
[rn/view {:style style/no-routes-found-container}
|
||||||
|
[quo/info-message
|
||||||
|
{:type :error
|
||||||
|
:icon :i/alert
|
||||||
|
:size :default
|
||||||
|
:style {:margin-top 15}}
|
||||||
|
(i18n/label :t/no-routes-found)]])
|
||||||
[quo/bottom-actions
|
[quo/bottom-actions
|
||||||
{:actions :one-action
|
{:actions :one-action
|
||||||
:button-one-label button-one-label
|
:button-one-label (if no-routes-found?
|
||||||
|
(i18n/label :t/try-again)
|
||||||
|
button-one-label)
|
||||||
:button-one-props (merge button-one-props
|
:button-one-props (merge button-one-props
|
||||||
{:disabled? confirm-disabled?
|
{:disabled? (and (not no-routes-found?) confirm-disabled?)
|
||||||
:on-press on-confirm})}]
|
:on-press (if no-routes-found?
|
||||||
|
#(rf/dispatch [:wallet/get-suggested-routes
|
||||||
|
{:amount (controlled-input/input-value
|
||||||
|
input-state)}])
|
||||||
|
on-confirm)}
|
||||||
|
(when no-routes-found?
|
||||||
|
{:type :grey}))}]
|
||||||
[quo/numbered-keyboard
|
[quo/numbered-keyboard
|
||||||
{:container-style (style/keyboard-container bottom)
|
{:container-style (style/keyboard-container bottom)
|
||||||
:left-action :dot
|
:left-action :dot
|
||||||
|
|
|
@ -3,20 +3,16 @@
|
||||||
|
|
||||||
(def routes-container
|
(def routes-container
|
||||||
{:padding-horizontal 20
|
{:padding-horizontal 20
|
||||||
:flex 1
|
:flex-grow 1
|
||||||
:padding-vertical 16
|
:padding-vertical 16
|
||||||
:width "100%"
|
:width "100%"})
|
||||||
:height "100%"})
|
|
||||||
|
|
||||||
(def routes-header-container
|
(def routes-header-container
|
||||||
{:flex-direction :row
|
{:flex-direction :row
|
||||||
:justify-content :space-between})
|
:justify-content :space-between})
|
||||||
|
|
||||||
(defn routes-inner-container
|
(def routes-inner-container
|
||||||
[first-item?]
|
{:flex-direction :row
|
||||||
{:margin-top (if first-item? 7.5 11)
|
|
||||||
:flex-direction :row
|
|
||||||
:align-items :center
|
|
||||||
:justify-content :space-between})
|
:justify-content :space-between})
|
||||||
|
|
||||||
(def section-label-right
|
(def section-label-right
|
||||||
|
@ -25,20 +21,26 @@
|
||||||
(def section-label-left
|
(def section-label-left
|
||||||
{:width 136})
|
{:width 136})
|
||||||
|
|
||||||
(def network-link
|
(def network-links-container
|
||||||
{:margin-horizontal -1.5
|
{:margin-horizontal -1.5
|
||||||
:z-index 1
|
:margin-top 7.5
|
||||||
|
:z-index 3
|
||||||
:flex 1})
|
:flex 1})
|
||||||
|
|
||||||
|
(defn network-link-container
|
||||||
|
[margin-top inverted?]
|
||||||
|
(cond-> {:position :absolute
|
||||||
|
:left 0
|
||||||
|
:right 0
|
||||||
|
:top margin-top}
|
||||||
|
inverted?
|
||||||
|
(assoc :transform [{:scaleY -1}])))
|
||||||
|
|
||||||
(def empty-container
|
(def empty-container
|
||||||
{:flex-grow 1
|
{:flex-grow 1
|
||||||
:align-items :center
|
:align-items :center
|
||||||
:justify-content :center})
|
:justify-content :center})
|
||||||
|
|
||||||
(def add-network
|
|
||||||
{:margin-top 11
|
|
||||||
:align-self :flex-end})
|
|
||||||
|
|
||||||
(defn warning-container
|
(defn warning-container
|
||||||
[color theme]
|
[color theme]
|
||||||
{:flex-direction :row
|
{:flex-direction :row
|
||||||
|
|
|
@ -6,19 +6,18 @@
|
||||||
[quo.foundations.resources :as resources]
|
[quo.foundations.resources :as resources]
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im.constants :as constants]
|
[status-im.contexts.wallet.common.utils.networks :as network-utils]
|
||||||
[status-im.contexts.wallet.common.utils.networks :as networks-utils]
|
|
||||||
[status-im.contexts.wallet.common.utils.send :as send-utils]
|
|
||||||
[status-im.contexts.wallet.send.routes.style :as style]
|
[status-im.contexts.wallet.send.routes.style :as style]
|
||||||
|
[status-im.contexts.wallet.send.utils :as send-utils]
|
||||||
[utils.debounce :as debounce]
|
[utils.debounce :as debounce]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]))
|
||||||
[utils.vector :as vector-utils]))
|
|
||||||
|
|
||||||
(def ^:private network-priority-score
|
(def row-height 44)
|
||||||
{:ethereum 1
|
(def space-between-rows 11)
|
||||||
:optimism 2
|
(def network-link-linear-height 10)
|
||||||
:arbitrum 3})
|
(def network-link-1x-height 56)
|
||||||
|
(def network-link-2x-height 111)
|
||||||
|
|
||||||
(defn- make-network-item
|
(defn- make-network-item
|
||||||
[{:keys [network-name chain-id] :as _network}
|
[{:keys [network-name chain-id] :as _network}
|
||||||
|
@ -33,40 +32,16 @@
|
||||||
:checked? (some #(= % chain-id) @network-preferences)
|
:checked? (some #(= % chain-id) @network-preferences)
|
||||||
:on-change on-change}})
|
:on-change on-change}})
|
||||||
|
|
||||||
(defn- find-network-link-insertion-index
|
(defn fetch-routes
|
||||||
[network-links chain-id loading-suggested-routes?]
|
[amount valid-input? bounce-duration-ms]
|
||||||
(let [network (networks-utils/id->network chain-id)
|
(if valid-input?
|
||||||
inserted-network-link-priority-score (network-priority-score network)]
|
(debounce/debounce-and-dispatch
|
||||||
(or (->> network-links
|
[:wallet/get-suggested-routes {:amount amount}]
|
||||||
(keep-indexed (fn [idx network-link]
|
bounce-duration-ms)
|
||||||
(let [network-link (networks-utils/id->network (if loading-suggested-routes?
|
(rf/dispatch [:wallet/clean-suggested-routes])))
|
||||||
network-link
|
|
||||||
(get-in network-link
|
|
||||||
[:from
|
|
||||||
:chain-id])))]
|
|
||||||
(when (> (network-priority-score network-link)
|
|
||||||
inserted-network-link-priority-score)
|
|
||||||
idx))))
|
|
||||||
first)
|
|
||||||
(count network-links))))
|
|
||||||
|
|
||||||
(defn- add-disabled-networks
|
|
||||||
[network-links disabled-from-networks loading-suggested-routes?]
|
|
||||||
(let [sorted-networks (sort-by (comp network-priority-score networks-utils/id->network)
|
|
||||||
disabled-from-networks)]
|
|
||||||
(reduce (fn [acc-network-links chain-id]
|
|
||||||
(let [index (find-network-link-insertion-index acc-network-links
|
|
||||||
chain-id
|
|
||||||
loading-suggested-routes?)
|
|
||||||
disabled-network-link {:status :disabled
|
|
||||||
:chain-id chain-id
|
|
||||||
:network (networks-utils/id->network chain-id)}]
|
|
||||||
(vector-utils/insert-element-at acc-network-links disabled-network-link index)))
|
|
||||||
network-links
|
|
||||||
sorted-networks)))
|
|
||||||
|
|
||||||
(defn networks-drawer
|
(defn networks-drawer
|
||||||
[{:keys [fetch-routes theme]}]
|
[{:keys [on-save theme]}]
|
||||||
(let [network-details (rf/sub [:wallet/network-details])
|
(let [network-details (rf/sub [:wallet/network-details])
|
||||||
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
|
{:keys [color]} (rf/sub [:wallet/current-viewing-account])
|
||||||
selected-networks (rf/sub [:wallet/wallet-send-receiver-networks])
|
selected-networks (rf/sub [:wallet/wallet-send-receiver-networks])
|
||||||
|
@ -119,188 +94,149 @@
|
||||||
(rf/dispatch [:wallet/update-receiver-networks
|
(rf/dispatch [:wallet/update-receiver-networks
|
||||||
@network-preferences])
|
@network-preferences])
|
||||||
(rf/dispatch [:hide-bottom-sheet])
|
(rf/dispatch [:hide-bottom-sheet])
|
||||||
(fetch-routes))
|
(on-save))
|
||||||
:customization-color color}}]])))
|
:customization-color color}}]])))
|
||||||
|
|
||||||
(defn route-item
|
(defn render-network-values
|
||||||
[{:keys [first-item? from-amount to-amount token-symbol from-chain-id to-chain-id from-network
|
[{:keys [network-values token-symbol on-press theme on-save to? loading-suggested-routes?]}]
|
||||||
to-network on-press-from-network on-press-to-network status theme fetch-routes disabled?
|
[rn/view
|
||||||
loading?]}]
|
(map-indexed (fn [index {:keys [chain-id total-amount type]}]
|
||||||
(if (= status :add)
|
[rn/view
|
||||||
[quo/network-bridge
|
{:key (str (if to? "to" "from") "-" chain-id)
|
||||||
{:status :add
|
:style {:margin-top (if (pos? index) 11 7.5)}}
|
||||||
:container-style style/add-network
|
[quo/network-bridge
|
||||||
:on-press #(rf/dispatch [:show-bottom-sheet
|
{:amount (str total-amount " " token-symbol)
|
||||||
{:content (fn [] [networks-drawer
|
:network (network-utils/id->network chain-id)
|
||||||
{:theme theme
|
:status type
|
||||||
:fetch-routes fetch-routes}])}])}]
|
:on-press #(when (not loading-suggested-routes?)
|
||||||
[rn/view {:style (style/routes-inner-container first-item?)}
|
(cond
|
||||||
[quo/network-bridge
|
(= type :add)
|
||||||
{:amount (str from-amount " " token-symbol)
|
(rf/dispatch [:show-bottom-sheet
|
||||||
:network from-network
|
{:content (fn []
|
||||||
:status status
|
[networks-drawer
|
||||||
:on-press #(when (and on-press-from-network (not loading?))
|
{:theme theme
|
||||||
(on-press-from-network from-chain-id from-amount))}]
|
:on-save on-save}])}])
|
||||||
(if (= status :default)
|
on-press (on-press chain-id total-amount)))}]])
|
||||||
[quo/network-link
|
network-values)])
|
||||||
{:shape :linear
|
|
||||||
:source from-network
|
|
||||||
:destination to-network
|
|
||||||
:container-style style/network-link}]
|
|
||||||
[rn/view {:style {:flex 1}}])
|
|
||||||
[quo/network-bridge
|
|
||||||
{:amount (str to-amount " " token-symbol)
|
|
||||||
:network to-network
|
|
||||||
:status (cond
|
|
||||||
(and disabled? loading?) :loading
|
|
||||||
(and disabled? (not loading?)) :default
|
|
||||||
:else status)
|
|
||||||
:on-press #(when (and on-press-to-network (not loading?))
|
|
||||||
(on-press-to-network to-chain-id to-amount))}]]))
|
|
||||||
|
|
||||||
(defn- render-network-link
|
(defn render-network-links
|
||||||
[item index _
|
[{:keys [network-links sender-network-values]}]
|
||||||
{:keys [from-values-by-chain to-values-by-chain theme fetch-routes on-press-from-network
|
[rn/view {:style style/network-links-container}
|
||||||
on-press-to-network token-symbol loading-suggested-routes?]}]
|
(map
|
||||||
(let [first-item? (zero? index)
|
(fn [{:keys [from-chain-id to-chain-id position-diff]}]
|
||||||
disabled-network? (= (:status item) :disabled)
|
(let [position-diff-absolute (js/Math.abs position-diff)
|
||||||
from-chain-id (get-in item [:from :chain-id])
|
shape (case position-diff-absolute
|
||||||
to-chain-id (get-in item [:to :chain-id])
|
0 :linear
|
||||||
from-amount (when from-chain-id
|
1 :1x
|
||||||
(from-values-by-chain from-chain-id))
|
2 :2x)
|
||||||
to-amount (when to-chain-id
|
height (case position-diff-absolute
|
||||||
(to-values-by-chain to-chain-id))]
|
0 network-link-linear-height
|
||||||
[route-item
|
1 network-link-1x-height
|
||||||
{:first-item? first-item?
|
2 network-link-2x-height)
|
||||||
:from-amount (if disabled-network? 0 from-amount)
|
inverted? (neg? position-diff)
|
||||||
:to-amount (if disabled-network? 0 to-amount)
|
source (network-utils/id->network from-chain-id)
|
||||||
:token-symbol token-symbol
|
destination (network-utils/id->network to-chain-id)
|
||||||
:disabled? disabled-network?
|
from-chain-id-index (first (keep-indexed #(when (= from-chain-id (:chain-id %2)) %1)
|
||||||
:loading? loading-suggested-routes?
|
sender-network-values))
|
||||||
:theme theme
|
base-margin-top (* (+ row-height space-between-rows)
|
||||||
:fetch-routes fetch-routes
|
from-chain-id-index)
|
||||||
:status (cond
|
margin-top (if (zero? position-diff)
|
||||||
(= (:status item) :add) :add
|
(+ base-margin-top
|
||||||
(= (:status item) :disabled) :disabled
|
(- (/ row-height 2) (/ height 2)))
|
||||||
loading-suggested-routes? :loading
|
(+ base-margin-top
|
||||||
:else :default)
|
(- (/ row-height 2) height)
|
||||||
:from-chain-id (or from-chain-id (:chain-id item))
|
(if inverted? height 0)))]
|
||||||
:to-chain-id (or to-chain-id (:chain-id item))
|
[rn/view
|
||||||
:from-network (cond (and loading-suggested-routes?
|
{:key (str "from-" from-chain-id "-to-" to-chain-id)
|
||||||
(not disabled-network?))
|
:style (style/network-link-container margin-top inverted?)}
|
||||||
(networks-utils/id->network item)
|
[rn/view {:style {:flex 1}}
|
||||||
disabled-network?
|
[quo/network-link
|
||||||
(networks-utils/id->network (:chain-id
|
{:shape shape
|
||||||
item))
|
:source source
|
||||||
:else
|
:destination destination}]]]))
|
||||||
(networks-utils/id->network from-chain-id))
|
network-links)])
|
||||||
:to-network (cond (and loading-suggested-routes?
|
|
||||||
(not disabled-network?))
|
|
||||||
(networks-utils/id->network item)
|
|
||||||
disabled-network?
|
|
||||||
(networks-utils/id->network (:chain-id
|
|
||||||
item))
|
|
||||||
:else
|
|
||||||
(networks-utils/id->network to-chain-id))
|
|
||||||
:on-press-from-network on-press-from-network
|
|
||||||
:on-press-to-network on-press-to-network}]))
|
|
||||||
|
|
||||||
(defn fetch-routes
|
(defn disable-chain
|
||||||
[amount valid-input? bounce-duration-ms]
|
[chain-id disabled-from-chain-ids token-available-networks-for-suggested-routes]
|
||||||
(if valid-input?
|
(let [disabled-chain-ids
|
||||||
(debounce/debounce-and-dispatch
|
(if (contains? (set
|
||||||
[:wallet/get-suggested-routes {:amount amount}]
|
disabled-from-chain-ids)
|
||||||
bounce-duration-ms)
|
chain-id)
|
||||||
(rf/dispatch [:wallet/clean-suggested-routes])))
|
(vec (remove #(= % chain-id)
|
||||||
|
disabled-from-chain-ids))
|
||||||
|
(conj disabled-from-chain-ids
|
||||||
|
chain-id))
|
||||||
|
re-enabling-chain?
|
||||||
|
(< (count disabled-chain-ids)
|
||||||
|
(count disabled-from-chain-ids))]
|
||||||
|
(if (or re-enabling-chain?
|
||||||
|
(> (count token-available-networks-for-suggested-routes) 1))
|
||||||
|
(rf/dispatch [:wallet/disable-from-networks
|
||||||
|
disabled-chain-ids])
|
||||||
|
(rf/dispatch [:toasts/upsert
|
||||||
|
{:id :disable-chain-error
|
||||||
|
:type :negative
|
||||||
|
:text (i18n/label :t/at-least-one-network-must-be-activated)}]))))
|
||||||
|
|
||||||
(defn view
|
(defn view
|
||||||
[{:keys [token theme input-value valid-input?
|
[{:keys [token theme input-value valid-input?
|
||||||
on-press-to-network current-screen-id]}]
|
on-press-to-network current-screen-id]}]
|
||||||
|
(let [token-symbol (:symbol token)
|
||||||
(let [token-symbol (:symbol token)
|
nav-current-screen-id (rf/sub [:view-id])
|
||||||
nav-current-screen-id (rf/sub [:view-id])
|
active-screen? (= nav-current-screen-id current-screen-id)
|
||||||
active-screen? (= nav-current-screen-id current-screen-id)
|
loading-suggested-routes? (rf/sub
|
||||||
loading-suggested-routes? (rf/sub
|
[:wallet/wallet-send-loading-suggested-routes?])
|
||||||
[:wallet/wallet-send-loading-suggested-routes?])
|
sender-network-values (rf/sub
|
||||||
from-values-by-chain (rf/sub
|
[:wallet/wallet-send-sender-network-values])
|
||||||
[:wallet/wallet-send-from-values-by-chain])
|
receiver-network-values (rf/sub
|
||||||
to-values-by-chain (rf/sub [:wallet/wallet-send-to-values-by-chain])
|
[:wallet/wallet-send-receiver-network-values])
|
||||||
suggested-routes (rf/sub [:wallet/wallet-send-suggested-routes])
|
network-links (rf/sub [:wallet/wallet-send-network-links])
|
||||||
selected-networks (rf/sub [:wallet/wallet-send-receiver-networks])
|
disabled-from-chain-ids (rf/sub
|
||||||
disabled-from-chain-ids (rf/sub
|
[:wallet/wallet-send-disabled-from-chain-ids])
|
||||||
[:wallet/wallet-send-disabled-from-chain-ids])
|
|
||||||
routes (when suggested-routes
|
|
||||||
(or (:best suggested-routes) []))
|
|
||||||
{token-balances-per-chain :balances-per-chain} (rf/sub
|
{token-balances-per-chain :balances-per-chain} (rf/sub
|
||||||
[:wallet/current-viewing-account-tokens-filtered
|
[:wallet/current-viewing-account-tokens-filtered
|
||||||
(str token-symbol)])
|
(str token-symbol)])
|
||||||
affordable-networks (send-utils/find-affordable-networks
|
token-available-networks-for-suggested-routes
|
||||||
{:balances-per-chain token-balances-per-chain
|
(send-utils/token-available-networks-for-suggested-routes
|
||||||
:input-value input-value
|
{:balances-per-chain token-balances-per-chain
|
||||||
:selected-networks selected-networks
|
:disabled-chain-ids disabled-from-chain-ids})
|
||||||
:disabled-chain-ids disabled-from-chain-ids})
|
show-routes? (not-empty sender-network-values)]
|
||||||
network-links (if loading-suggested-routes?
|
|
||||||
affordable-networks
|
|
||||||
routes)
|
|
||||||
show-routes? (or (and (not-empty affordable-networks)
|
|
||||||
loading-suggested-routes?)
|
|
||||||
(not-empty routes))]
|
|
||||||
|
|
||||||
(rn/use-effect
|
(rn/use-effect
|
||||||
#(when (and active-screen? (> (count affordable-networks) 0))
|
#(when (and active-screen? (> (count token-available-networks-for-suggested-routes) 0))
|
||||||
(fetch-routes input-value valid-input? 2000))
|
(fetch-routes input-value valid-input? 2000))
|
||||||
[input-value valid-input?])
|
[input-value valid-input?])
|
||||||
(rn/use-effect
|
(rn/use-effect
|
||||||
#(when (and active-screen? (> (count affordable-networks) 0))
|
#(when (and active-screen? (> (count token-available-networks-for-suggested-routes) 0))
|
||||||
(fetch-routes input-value valid-input? 0))
|
(fetch-routes input-value valid-input? 0))
|
||||||
[disabled-from-chain-ids])
|
[disabled-from-chain-ids])
|
||||||
(if show-routes?
|
[rn/scroll-view {:content-container-style style/routes-container}
|
||||||
(let [initial-network-links-count (count network-links)
|
(when show-routes?
|
||||||
disabled-count (count disabled-from-chain-ids)
|
[rn/view {:style style/routes-header-container}
|
||||||
network-links (if (not-empty disabled-from-chain-ids)
|
[quo/section-label
|
||||||
(add-disabled-networks network-links
|
{:section (i18n/label :t/from-label)
|
||||||
disabled-from-chain-ids
|
:container-style style/section-label-left}]
|
||||||
loading-suggested-routes?)
|
[quo/section-label
|
||||||
network-links)
|
{:section (i18n/label :t/to-label)
|
||||||
network-links-with-add-button (if (and (< (- (count network-links) disabled-count)
|
:container-style style/section-label-right}]])
|
||||||
constants/default-network-count)
|
[rn/view {:style style/routes-inner-container}
|
||||||
(pos? initial-network-links-count))
|
[render-network-values
|
||||||
(concat network-links [{:status :add}])
|
{:token-symbol token-symbol
|
||||||
network-links)]
|
:network-values sender-network-values
|
||||||
[rn/flat-list
|
:on-press #(disable-chain %1
|
||||||
{:data network-links-with-add-button
|
disabled-from-chain-ids
|
||||||
:content-container-style style/routes-container
|
token-available-networks-for-suggested-routes)
|
||||||
:header [rn/view {:style style/routes-header-container}
|
:to? false
|
||||||
[quo/section-label
|
:theme theme
|
||||||
{:section (i18n/label :t/from-label)
|
:loading-suggested-routes? loading-suggested-routes?}]
|
||||||
:container-style style/section-label-left}]
|
[render-network-links
|
||||||
[quo/section-label
|
{:network-links network-links
|
||||||
{:section (i18n/label :t/to-label)
|
:sender-network-values sender-network-values}]
|
||||||
:container-style style/section-label-right}]]
|
[render-network-values
|
||||||
:render-data
|
{:token-symbol token-symbol
|
||||||
{:from-values-by-chain from-values-by-chain
|
:network-values receiver-network-values
|
||||||
:to-values-by-chain to-values-by-chain
|
:on-press on-press-to-network
|
||||||
:theme theme
|
:to? true
|
||||||
:fetch-routes #(fetch-routes % valid-input? 2000)
|
:loading-suggested-routes? loading-suggested-routes?
|
||||||
:on-press-from-network (fn [chain-id _]
|
:theme theme
|
||||||
(let [disabled-chain-ids (if (contains? (set
|
:on-save #(fetch-routes input-value valid-input? 0)}]]]))
|
||||||
disabled-from-chain-ids)
|
|
||||||
chain-id)
|
|
||||||
(vec (remove #(= % chain-id)
|
|
||||||
disabled-from-chain-ids))
|
|
||||||
(conj disabled-from-chain-ids
|
|
||||||
chain-id))
|
|
||||||
re-enabling-chain? (< (count disabled-chain-ids)
|
|
||||||
(count disabled-from-chain-ids))]
|
|
||||||
(when (or re-enabling-chain?
|
|
||||||
(> (count affordable-networks) 1))
|
|
||||||
(rf/dispatch [:wallet/disable-from-networks
|
|
||||||
disabled-chain-ids]))))
|
|
||||||
:on-press-to-network on-press-to-network
|
|
||||||
:token-symbol token-symbol
|
|
||||||
:loading-suggested-routes? loading-suggested-routes?}
|
|
||||||
:render-fn render-network-link}])
|
|
||||||
[rn/view {:style style/empty-container}
|
|
||||||
(when (and (not (nil? routes)) (not loading-suggested-routes?))
|
|
||||||
[quo/text (i18n/label :t/no-routes-found)])])))
|
|
||||||
|
|
||||||
|
|
|
@ -209,9 +209,7 @@
|
||||||
|
|
||||||
(defn view
|
(defn view
|
||||||
[_]
|
[_]
|
||||||
(let [on-close (fn []
|
(let [on-close #(rf/dispatch [:navigate-back])]
|
||||||
(rf/dispatch [:wallet/clean-suggested-routes])
|
|
||||||
(rf/dispatch [:navigate-back]))]
|
|
||||||
(fn []
|
(fn []
|
||||||
(let [theme (quo.theme/use-theme)
|
(let [theme (quo.theme/use-theme)
|
||||||
send-transaction-data (rf/sub [:wallet/wallet-send])
|
send-transaction-data (rf/sub [:wallet/wallet-send])
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
(:require
|
(:require
|
||||||
[legacy.status-im.utils.hex :as utils.hex]
|
[legacy.status-im.utils.hex :as utils.hex]
|
||||||
[native-module.core :as native-module]
|
[native-module.core :as native-module]
|
||||||
|
[status-im.contexts.wallet.common.utils.networks :as network-utils]
|
||||||
[utils.money :as money]))
|
[utils.money :as money]))
|
||||||
|
|
||||||
(defn amount-in-hex
|
(defn amount-in-hex
|
||||||
|
@ -24,6 +25,23 @@
|
||||||
{}
|
{}
|
||||||
transaction-hashes))
|
transaction-hashes))
|
||||||
|
|
||||||
|
(defn calculate-gas-fee
|
||||||
|
[data]
|
||||||
|
(let [gas-amount (money/bignumber (get data :gas-amount))
|
||||||
|
gas-fees (get data :gas-fees)
|
||||||
|
eip1559-enabled? (get gas-fees :eip-1559-enabled)
|
||||||
|
optimal-price-gwei (money/bignumber (if eip1559-enabled?
|
||||||
|
(get gas-fees :max-fee-per-gas-medium)
|
||||||
|
(get gas-fees :gas-price)))
|
||||||
|
total-gas-fee-wei (money/mul (money/->wei :gwei optimal-price-gwei) gas-amount)
|
||||||
|
l1-fee-wei (money/->wei :gwei (get gas-fees :l-1-gas-fee))]
|
||||||
|
(money/add total-gas-fee-wei l1-fee-wei)))
|
||||||
|
|
||||||
|
(defn calculate-full-route-gas-fee
|
||||||
|
"Sums all the routes fees in wei and then convert the total value to ether"
|
||||||
|
[route]
|
||||||
|
(money/wei->ether (reduce money/add (map calculate-gas-fee route))))
|
||||||
|
|
||||||
(defn network-amounts-by-chain
|
(defn network-amounts-by-chain
|
||||||
[{:keys [route token-decimals native-token? to?]}]
|
[{:keys [route token-decimals native-token? to?]}]
|
||||||
(reduce (fn [acc path]
|
(reduce (fn [acc path]
|
||||||
|
@ -47,3 +65,104 @@
|
||||||
(assoc acc k (if (money/equal-to v 0) "<0.01" v)))
|
(assoc acc k (if (money/equal-to v 0) "<0.01" v)))
|
||||||
{}
|
{}
|
||||||
amounts))
|
amounts))
|
||||||
|
|
||||||
|
(defn token-available-networks-for-suggested-routes
|
||||||
|
[{:keys [balances-per-chain disabled-chain-ids]}]
|
||||||
|
(let [disabled-set (set disabled-chain-ids)]
|
||||||
|
(->> balances-per-chain
|
||||||
|
(filter (fn [[_ {:keys [chain-id]}]]
|
||||||
|
(not (contains? disabled-set chain-id))))
|
||||||
|
(map first))))
|
||||||
|
|
||||||
|
(def ^:private network-priority-score
|
||||||
|
{:ethereum 1
|
||||||
|
:optimism 2
|
||||||
|
:arbitrum 3})
|
||||||
|
|
||||||
|
(def ^:private available-networks-count
|
||||||
|
(count (set (keys network-priority-score))))
|
||||||
|
|
||||||
|
(defn reset-network-amounts-to-zero
|
||||||
|
[network-amounts]
|
||||||
|
(map
|
||||||
|
(fn [network-amount]
|
||||||
|
(cond-> network-amount
|
||||||
|
(= (:type network-amount) :loading)
|
||||||
|
(assoc :total-amount (money/bignumber "0")
|
||||||
|
:type :default)))
|
||||||
|
network-amounts))
|
||||||
|
|
||||||
|
(defn network-amounts
|
||||||
|
[network-values disabled-chain-ids receiver-networks to?]
|
||||||
|
(let [disabled-set (set disabled-chain-ids)
|
||||||
|
receiver-networks-set (set receiver-networks)
|
||||||
|
network-values-keys (set (keys network-values))
|
||||||
|
routes-found? (pos? (count network-values-keys))
|
||||||
|
updated-network-values (when routes-found?
|
||||||
|
(reduce (fn [acc k]
|
||||||
|
(if (or (contains? network-values-keys k)
|
||||||
|
(and to?
|
||||||
|
(not (contains? receiver-networks-set k))))
|
||||||
|
acc
|
||||||
|
(assoc acc k (money/bignumber "0"))))
|
||||||
|
network-values
|
||||||
|
disabled-chain-ids))]
|
||||||
|
(cond-> (->> updated-network-values
|
||||||
|
(map
|
||||||
|
(fn [[k v]]
|
||||||
|
{:chain-id k
|
||||||
|
:total-amount v
|
||||||
|
:type (if (or to? (not (contains? disabled-set k))) :default :disabled)}))
|
||||||
|
(sort-by #(get network-priority-score (network-utils/id->network (:chain-id %))))
|
||||||
|
(filter
|
||||||
|
#(or (and to?
|
||||||
|
(or (contains? receiver-networks-set (:chain-id %))
|
||||||
|
(money/greater-than (:total-amount %) (money/bignumber "0"))))
|
||||||
|
(not to?)))
|
||||||
|
(vec))
|
||||||
|
(and to?
|
||||||
|
routes-found?
|
||||||
|
(< (count updated-network-values) available-networks-count))
|
||||||
|
(conj {:type :add}))))
|
||||||
|
|
||||||
|
(defn loading-network-amounts
|
||||||
|
[valid-networks disabled-chain-ids receiver-networks to?]
|
||||||
|
(let [disabled-set (set disabled-chain-ids)
|
||||||
|
receiver-networks-set (set receiver-networks)
|
||||||
|
receiver-networks-count (count receiver-networks)
|
||||||
|
valid-networks (concat valid-networks disabled-chain-ids)]
|
||||||
|
(cond-> (->> valid-networks
|
||||||
|
(map (fn [k]
|
||||||
|
(cond-> {:chain-id k
|
||||||
|
:type (if (or to?
|
||||||
|
(not (contains? disabled-set k)))
|
||||||
|
:loading
|
||||||
|
:disabled)}
|
||||||
|
(and (not to?) (contains? disabled-set k))
|
||||||
|
(assoc :total-amount (money/bignumber "0")))))
|
||||||
|
(sort-by (fn [item]
|
||||||
|
(get network-priority-score
|
||||||
|
(network-utils/id->network (:chain-id item)))))
|
||||||
|
(filter
|
||||||
|
#(or (and to? (contains? receiver-networks-set (:chain-id %)))
|
||||||
|
(and (not to?)
|
||||||
|
(not (contains? disabled-chain-ids (:chain-id %))))))
|
||||||
|
(vec))
|
||||||
|
(and to? (< receiver-networks-count available-networks-count)) (conj {:type :add}))))
|
||||||
|
|
||||||
|
(defn network-links
|
||||||
|
[route from-values-by-chain to-values-by-chain]
|
||||||
|
(reduce (fn [acc path]
|
||||||
|
(let [from-chain-id (get-in path [:from :chain-id])
|
||||||
|
to-chain-id (get-in path [:to :chain-id])
|
||||||
|
from-chain-id-index (first (keep-indexed #(when (= from-chain-id (:chain-id %2)) %1)
|
||||||
|
from-values-by-chain))
|
||||||
|
to-chain-id-index (first (keep-indexed #(when (= to-chain-id (:chain-id %2)) %1)
|
||||||
|
to-values-by-chain))
|
||||||
|
position-diff (- from-chain-id-index to-chain-id-index)]
|
||||||
|
(conj acc
|
||||||
|
{:from-chain-id from-chain-id
|
||||||
|
:to-chain-id to-chain-id
|
||||||
|
:position-diff position-diff})))
|
||||||
|
[]
|
||||||
|
route))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
(ns status-im.contexts.wallet.send.utils-test
|
(ns status-im.contexts.wallet.send.utils-test
|
||||||
(:require [cljs.test :refer [deftest is testing]]
|
(:require [cljs.test :refer [deftest is testing]]
|
||||||
[status-im.contexts.wallet.send.utils :as utils]
|
[status-im.contexts.wallet.send.utils :as utils]
|
||||||
|
[utils.map :as map]
|
||||||
[utils.money :as money]))
|
[utils.money :as money]))
|
||||||
|
|
||||||
(deftest test-amount-in-hex
|
(deftest test-amount-in-hex
|
||||||
|
@ -93,3 +94,350 @@
|
||||||
(doseq [[chain-id exp-value] expected]
|
(doseq [[chain-id exp-value] expected]
|
||||||
(is #(or (= (get result chain-id) exp-value)
|
(is #(or (= (get result chain-id) exp-value)
|
||||||
(money/equal-to (get result chain-id) exp-value)))))))
|
(money/equal-to (get result chain-id) exp-value)))))))
|
||||||
|
|
||||||
|
(deftest test-calculate-gas-fee
|
||||||
|
(testing "EIP-1559 transaction without L1 fee"
|
||||||
|
(let [data {:gas-amount "23487"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "0"}}
|
||||||
|
expected-result (money/bignumber "53063589834657")] ; This is in Wei
|
||||||
|
(is (money/equal-to (utils/calculate-gas-fee data)
|
||||||
|
expected-result))))
|
||||||
|
|
||||||
|
(testing "EIP-1559 transaction with L1 fee of 60,000 Gwei"
|
||||||
|
(let [data {:gas-amount "23487"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "60000"}}
|
||||||
|
expected-result (money/bignumber "113063589834657")] ; Added 60,000 Gwei in Wei to the
|
||||||
|
; previous result
|
||||||
|
(is (money/equal-to (utils/calculate-gas-fee data)
|
||||||
|
expected-result))))
|
||||||
|
|
||||||
|
(testing "Non-EIP-1559 transaction with specified gas price"
|
||||||
|
(let [data {:gas-amount "23487"
|
||||||
|
:gas-fees {:gas-price "2.872721089"
|
||||||
|
:eip-1559-enabled false
|
||||||
|
:l-1-gas-fee "0"}}
|
||||||
|
expected-result (money/bignumber "67471600217343")] ; This is in Wei, for the specified
|
||||||
|
; gas amount and price
|
||||||
|
(is (money/equal-to (utils/calculate-gas-fee data)
|
||||||
|
expected-result)))))
|
||||||
|
|
||||||
|
(deftest test-calculate-full-route-gas-fee
|
||||||
|
(testing "Route with a single EIP-1559 transaction, no L1 fees"
|
||||||
|
(let [route [{:gas-amount "23487"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "0"}}]
|
||||||
|
expected-result (money/bignumber "0.000053063589834657")] ; The Wei amount for the
|
||||||
|
; transaction, converted to
|
||||||
|
; Ether
|
||||||
|
(is (money/equal-to (utils/calculate-full-route-gas-fee route)
|
||||||
|
expected-result))))
|
||||||
|
|
||||||
|
(testing "Route with two EIP-1559 transactions, no L1 fees"
|
||||||
|
(let [route [{:gas-amount "23487"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "0"}}
|
||||||
|
{:gas-amount "23487"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "0"}}]
|
||||||
|
expected-result (money/bignumber "0.000106127179669314")] ; Sum of both transactions' Wei
|
||||||
|
; amounts, converted to Ether
|
||||||
|
(is (money/equal-to (utils/calculate-full-route-gas-fee route)
|
||||||
|
expected-result))))
|
||||||
|
|
||||||
|
(testing "Route with two EIP-1559 transactions, one with L1 fee of 60,000 Gwei"
|
||||||
|
(let [route [{:gas-amount "23487"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "0"}}
|
||||||
|
{:gas-amount "23487"
|
||||||
|
:gas-fees {:max-fee-per-gas-medium "2.259274911"
|
||||||
|
:eip-1559-enabled true
|
||||||
|
:l-1-gas-fee "60000"}}]
|
||||||
|
expected-result (money/bignumber "0.000166127179669314")] ; Added 60,000 Gwei in Wei to
|
||||||
|
; the previous total and
|
||||||
|
; converted to Ether
|
||||||
|
(is (money/equal-to (utils/calculate-full-route-gas-fee route)
|
||||||
|
expected-result)))))
|
||||||
|
|
||||||
|
(deftest test-token-available-networks-for-suggested-routes
|
||||||
|
(testing "Excludes disabled chain-ids correctly"
|
||||||
|
(let [balances-per-chain {"1" {:chain-id "1" :balance 100}
|
||||||
|
"10" {:chain-id "10" :balance 200}
|
||||||
|
"42161" {:chain-id "42161" :balance 300}}
|
||||||
|
disabled-chain-ids ["10"]
|
||||||
|
expected ["1" "42161"]]
|
||||||
|
(is (= expected
|
||||||
|
(utils/token-available-networks-for-suggested-routes {:balances-per-chain balances-per-chain
|
||||||
|
:disabled-chain-ids
|
||||||
|
disabled-chain-ids})))))
|
||||||
|
|
||||||
|
(testing "Returns all chains when no disabled chains are specified"
|
||||||
|
(let [balances-per-chain {"1" {:chain-id "1" :balance 100}
|
||||||
|
"10" {:chain-id "10" :balance 200}
|
||||||
|
"42161" {:chain-id "42161" :balance 300}}
|
||||||
|
disabled-chain-ids []
|
||||||
|
expected ["1" "10" "42161"]]
|
||||||
|
(is (= expected
|
||||||
|
(utils/token-available-networks-for-suggested-routes {:balances-per-chain balances-per-chain
|
||||||
|
:disabled-chain-ids
|
||||||
|
disabled-chain-ids})))))
|
||||||
|
|
||||||
|
(testing "Returns empty list when all chains are disabled"
|
||||||
|
(let [balances-per-chain {"1" {:chain-id "1" :balance 100}
|
||||||
|
"10" {:chain-id "10" :balance 200}
|
||||||
|
"42161" {:chain-id "42161" :balance 300}}
|
||||||
|
disabled-chain-ids ["1" "10" "42161"]
|
||||||
|
expected []]
|
||||||
|
(is (= expected
|
||||||
|
(utils/token-available-networks-for-suggested-routes {:balances-per-chain balances-per-chain
|
||||||
|
:disabled-chain-ids
|
||||||
|
disabled-chain-ids})))))
|
||||||
|
|
||||||
|
(testing "Handles non-existent chain-ids gracefully"
|
||||||
|
(let [balances-per-chain {"59144" {:chain-id "59144" :balance 400}}
|
||||||
|
disabled-chain-ids ["1" "10" "42161"]
|
||||||
|
expected ["59144"]]
|
||||||
|
(is (= expected
|
||||||
|
(utils/token-available-networks-for-suggested-routes {:balances-per-chain balances-per-chain
|
||||||
|
:disabled-chain-ids
|
||||||
|
disabled-chain-ids}))))))
|
||||||
|
|
||||||
|
(deftest test-reset-network-amounts-to-zero
|
||||||
|
(testing "Correctly resets loading network amounts to zero and changes type to default"
|
||||||
|
(let [network-amounts [{:chain-id "1" :total-amount (money/bignumber "100") :type :loading}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "200") :type :default}]
|
||||||
|
expected [{:chain-id "1" :total-amount (money/bignumber "0") :type :default}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "200") :type :default}]
|
||||||
|
result (utils/reset-network-amounts-to-zero network-amounts)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons))))
|
||||||
|
|
||||||
|
(testing "Leaves non-loading types unchanged"
|
||||||
|
(let [network-amounts [{:chain-id "1" :total-amount (money/bignumber "100") :type :default}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "0") :type :disabled}]
|
||||||
|
expected [{:chain-id "1" :total-amount (money/bignumber "100") :type :default}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "0") :type :disabled}]
|
||||||
|
result (utils/reset-network-amounts-to-zero network-amounts)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons))))
|
||||||
|
|
||||||
|
(testing "Processes an empty list without error"
|
||||||
|
(let [network-amounts []
|
||||||
|
expected []
|
||||||
|
result (utils/reset-network-amounts-to-zero network-amounts)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons))))
|
||||||
|
|
||||||
|
(testing "Applies transformations to multiple loading entries"
|
||||||
|
(let [network-amounts [{:chain-id "1" :total-amount (money/bignumber "100") :type :loading}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "200") :type :loading}]
|
||||||
|
expected [{:chain-id "1" :total-amount (money/bignumber "0") :type :default}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "0") :type :default}]
|
||||||
|
result (utils/reset-network-amounts-to-zero network-amounts)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons))))
|
||||||
|
|
||||||
|
(testing "Mix of loading and non-loading types"
|
||||||
|
(let [network-amounts [{:chain-id "1" :total-amount (money/bignumber "100") :type :loading}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "200") :type :default}
|
||||||
|
{:chain-id "42161" :total-amount (money/bignumber "300") :type :loading}
|
||||||
|
{:chain-id "59144" :total-amount (money/bignumber "0") :type :disabled}]
|
||||||
|
expected [{:chain-id "1" :total-amount (money/bignumber "0") :type :default}
|
||||||
|
{:chain-id "10" :total-amount (money/bignumber "200") :type :default}
|
||||||
|
{:chain-id "42161" :total-amount (money/bignumber "0") :type :default}
|
||||||
|
{:chain-id "59144" :total-amount (money/bignumber "0") :type :disabled}]
|
||||||
|
result (utils/reset-network-amounts-to-zero network-amounts)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons)))))
|
||||||
|
|
||||||
|
(deftest test-network-amounts
|
||||||
|
(testing "Handles disabled and receiver networks correctly when to? is true"
|
||||||
|
(let [network-values {"1" (money/bignumber "100")
|
||||||
|
"10" (money/bignumber "200")}
|
||||||
|
disabled-chain-ids ["1"]
|
||||||
|
receiver-networks ["10"]
|
||||||
|
to? true
|
||||||
|
expected [{:chain-id "1"
|
||||||
|
:total-amount (money/bignumber "100")
|
||||||
|
:type :default}
|
||||||
|
{:chain-id "10"
|
||||||
|
:total-amount (money/bignumber "200")
|
||||||
|
:type :default}
|
||||||
|
{:type :add}]
|
||||||
|
result (utils/network-amounts network-values
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)]
|
||||||
|
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||||
|
|
||||||
|
(testing "Adds default amount for non-disabled non-receiver networks when to? is false"
|
||||||
|
(let [network-values {"1" (money/bignumber "100")}
|
||||||
|
disabled-chain-ids ["10"]
|
||||||
|
receiver-networks []
|
||||||
|
to? false
|
||||||
|
expected [{:chain-id "1"
|
||||||
|
:total-amount (money/bignumber "100")
|
||||||
|
:type :default}
|
||||||
|
{:chain-id "10"
|
||||||
|
:total-amount (money/bignumber "0")
|
||||||
|
:type :disabled}]
|
||||||
|
result (utils/network-amounts network-values
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)]
|
||||||
|
(is (every? identity (map #(map/deep-compare %1 %2) expected result)))))
|
||||||
|
|
||||||
|
(testing "Handles empty inputs correctly"
|
||||||
|
(let [network-values {}
|
||||||
|
disabled-chain-ids []
|
||||||
|
receiver-networks []
|
||||||
|
to? true
|
||||||
|
expected []
|
||||||
|
result (utils/network-amounts network-values
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)]
|
||||||
|
(is (= expected result))))
|
||||||
|
|
||||||
|
(testing "Processes case with multiple network interactions"
|
||||||
|
(let [network-values {"1" (money/bignumber "300")
|
||||||
|
"10" (money/bignumber "400")
|
||||||
|
"42161" (money/bignumber "500")}
|
||||||
|
disabled-chain-ids ["1" "42161"]
|
||||||
|
receiver-networks ["10"]
|
||||||
|
to? true
|
||||||
|
expected [{:chain-id "1"
|
||||||
|
:total-amount (money/bignumber "300")
|
||||||
|
:type :default}
|
||||||
|
{:chain-id "10"
|
||||||
|
:total-amount (money/bignumber "400")
|
||||||
|
:type :default}
|
||||||
|
{:chain-id "42161"
|
||||||
|
:total-amount (money/bignumber "500")
|
||||||
|
:type :default}]
|
||||||
|
result (utils/network-amounts network-values
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)]
|
||||||
|
(is (every? identity (map #(map/deep-compare %1 %2) expected result))))))
|
||||||
|
|
||||||
|
(deftest test-loading-network-amounts
|
||||||
|
(testing "Assigns :loading type to valid networks except for disabled ones"
|
||||||
|
(let [valid-networks ["1" "10" "42161"]
|
||||||
|
disabled-chain-ids ["42161"]
|
||||||
|
receiver-networks ["1" "10"]
|
||||||
|
to? true
|
||||||
|
expected [{:chain-id "1" :type :loading}
|
||||||
|
{:chain-id "10" :type :loading}
|
||||||
|
{:type :add}]
|
||||||
|
result (utils/loading-network-amounts valid-networks
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons))))
|
||||||
|
|
||||||
|
(testing "Assigns :disabled type with zero total-amount to disabled networks when to? is false"
|
||||||
|
(let [valid-networks ["1" "10" "42161"]
|
||||||
|
disabled-chain-ids ["10" "42161"]
|
||||||
|
receiver-networks ["1"]
|
||||||
|
to? false
|
||||||
|
expected [{:chain-id "1" :type :loading}
|
||||||
|
{:chain-id "10" :type :disabled :total-amount (money/bignumber "0")}
|
||||||
|
{:chain-id "42161" :type :disabled :total-amount (money/bignumber "0")}]
|
||||||
|
result (utils/loading-network-amounts valid-networks
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons))))
|
||||||
|
|
||||||
|
(testing "Filters out networks not in receiver networks when to? is true"
|
||||||
|
(let [valid-networks ["1" "10" "42161" "59144"]
|
||||||
|
disabled-chain-ids ["10"]
|
||||||
|
receiver-networks ["1" "42161"]
|
||||||
|
to? true
|
||||||
|
expected [{:chain-id "1" :type :loading}
|
||||||
|
{:chain-id "42161" :type :loading}]
|
||||||
|
result (utils/loading-network-amounts valid-networks
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons))))
|
||||||
|
|
||||||
|
(testing "Appends :add type if receiver network count is less than available networks and to? is true"
|
||||||
|
(let [valid-networks ["1" "10" "42161"]
|
||||||
|
disabled-chain-ids ["10"]
|
||||||
|
receiver-networks ["1"]
|
||||||
|
to? true
|
||||||
|
expected [{:chain-id "1" :type :loading}
|
||||||
|
{:type :add}]
|
||||||
|
result (utils/loading-network-amounts valid-networks
|
||||||
|
disabled-chain-ids
|
||||||
|
receiver-networks
|
||||||
|
to?)
|
||||||
|
comparisons (map #(map/deep-compare %1 %2)
|
||||||
|
expected
|
||||||
|
result)]
|
||||||
|
(is (every? identity comparisons)))))
|
||||||
|
|
||||||
|
(deftest test-network-links
|
||||||
|
(testing "Calculates position differences correctly"
|
||||||
|
(let [route [{:from {:chain-id "1"} :to {:chain-id "42161"}}
|
||||||
|
{:from {:chain-id "10"} :to {:chain-id "1"}}
|
||||||
|
{:from {:chain-id "42161"} :to {:chain-id "10"}}]
|
||||||
|
from-values-by-chain [{:chain-id "1"} {:chain-id "10"} {:chain-id "42161"}]
|
||||||
|
to-values-by-chain [{:chain-id "42161"} {:chain-id "1"} {:chain-id "10"}]
|
||||||
|
expected [{:from-chain-id "1" :to-chain-id "42161" :position-diff 0}
|
||||||
|
{:from-chain-id "10" :to-chain-id "1" :position-diff 0}
|
||||||
|
{:from-chain-id "42161" :to-chain-id "10" :position-diff 0}]
|
||||||
|
result (utils/network-links route from-values-by-chain to-values-by-chain)]
|
||||||
|
(is (= expected result))))
|
||||||
|
|
||||||
|
(testing "Handles cases with no position difference"
|
||||||
|
(let [route [{:from {:chain-id "1"} :to {:chain-id "1"}}]
|
||||||
|
from-values-by-chain [{:chain-id "1"} {:chain-id "10"} {:chain-id "42161"}]
|
||||||
|
to-values-by-chain [{:chain-id "1"} {:chain-id "10"} {:chain-id "42161"}]
|
||||||
|
expected [{:from-chain-id "1" :to-chain-id "1" :position-diff 0}]
|
||||||
|
result (utils/network-links route from-values-by-chain to-values-by-chain)]
|
||||||
|
(is (= expected result))))
|
||||||
|
|
||||||
|
(testing "Handles empty route"
|
||||||
|
(let [route []
|
||||||
|
from-values-by-chain []
|
||||||
|
to-values-by-chain []
|
||||||
|
expected []
|
||||||
|
result (utils/network-links route from-values-by-chain to-values-by-chain)]
|
||||||
|
(is (= expected result))))
|
||||||
|
|
||||||
|
(testing "Verifies negative position differences"
|
||||||
|
(let [route [{:from {:chain-id "1"} :to {:chain-id "42161"}}]
|
||||||
|
from-values-by-chain [{:chain-id "1"} {:chain-id "10"} {:chain-id "42161"}]
|
||||||
|
to-values-by-chain [{:chain-id "1"} {:chain-id "10"} {:chain-id "42161"}]
|
||||||
|
expected [{:from-chain-id "1" :to-chain-id "42161" :position-diff -2}]
|
||||||
|
result (utils/network-links route from-values-by-chain to-values-by-chain)]
|
||||||
|
(is (= expected result)))))
|
||||||
|
|
|
@ -121,6 +121,21 @@
|
||||||
:<- [:wallet/wallet-send]
|
:<- [:wallet/wallet-send]
|
||||||
:-> :suggested-routes)
|
:-> :suggested-routes)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/wallet-send-sender-network-values
|
||||||
|
:<- [:wallet/wallet-send]
|
||||||
|
:-> :sender-network-values)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/wallet-send-receiver-network-values
|
||||||
|
:<- [:wallet/wallet-send]
|
||||||
|
:-> :receiver-network-values)
|
||||||
|
|
||||||
|
(rf/reg-sub
|
||||||
|
:wallet/wallet-send-network-links
|
||||||
|
:<- [:wallet/wallet-send]
|
||||||
|
:-> :network-links)
|
||||||
|
|
||||||
(rf/reg-sub
|
(rf/reg-sub
|
||||||
:wallet/keypairs
|
:wallet/keypairs
|
||||||
:<- [:wallet]
|
:<- [:wallet]
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
(ns utils.map
|
||||||
|
(:require [utils.money :as money]))
|
||||||
|
|
||||||
|
(defn compare-values
|
||||||
|
"Compares two values, using special handling for BigNumbers and regular equality for others."
|
||||||
|
[v1 v2]
|
||||||
|
(cond
|
||||||
|
(and (money/bignumber? v1) (money/bignumber? v2)) (money/equal-to v1 v2)
|
||||||
|
:else (= v1 v2)))
|
||||||
|
|
||||||
|
(defn deep-compare
|
||||||
|
"Recursively compare two maps, specially handling BigNumber values within the maps."
|
||||||
|
[map1 map2]
|
||||||
|
(and
|
||||||
|
(= (set (keys map1)) (set (keys map2)))
|
||||||
|
(every? (fn [k] (compare-values (get map1 k) (get map2 k)))
|
||||||
|
(keys map1))))
|
|
@ -37,6 +37,11 @@
|
||||||
(new BigNumber (normalize (str n)))
|
(new BigNumber (normalize (str n)))
|
||||||
(catch :default _ nil))))
|
(catch :default _ nil))))
|
||||||
|
|
||||||
|
(defn bignumber?
|
||||||
|
"Check if the value is a bignumber."
|
||||||
|
[x]
|
||||||
|
(instance? BigNumber x))
|
||||||
|
|
||||||
(defn greater-than-or-equals
|
(defn greater-than-or-equals
|
||||||
[^js bn1 ^js bn2]
|
[^js bn1 ^js bn2]
|
||||||
(.greaterThanOrEqualTo bn1 bn2))
|
(.greaterThanOrEqualTo bn1 bn2))
|
||||||
|
|
|
@ -2595,5 +2595,6 @@
|
||||||
"this-account-has-no-activity": "This account has no activity",
|
"this-account-has-no-activity": "This account has no activity",
|
||||||
"this-address-has-activity": "This address has activity",
|
"this-address-has-activity": "This address has activity",
|
||||||
"scanning-for-activity": "Scanning for activity...",
|
"scanning-for-activity": "Scanning for activity...",
|
||||||
"send-community-link": "Send community link"
|
"send-community-link": "Send community link",
|
||||||
|
"at-least-one-network-must-be-activated": "At least 1 network must be activated"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue