Compare commits

..
Author SHA1 Message Date
Mohamed JavidandYevheniia Berdnyk f69fa8ba58 feat(wallet)_: Add base chain indicator (#22215)
* feat(wallet)_: Add base chain indicator

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>

* e2e: updated Base network name

---------

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
Co-authored-by: Yevheniia Berdnyk <ie.berdnyk@gmail.com>
2025-03-04 02:03:06 +05:30
Parvesh Monu 714d966d48 fix Some of the message longtap options disappear until relogin after unmuting chat (#22220) (#22221) 2025-03-04 00:05:41 +05:30
Siddarth Kumar 11d66e5bc2 ci: fix nix-cache-job with nix-export 2025-03-03 15:39:12 +05:30
41 changed files with 313 additions and 444 deletions
+12 -3
View File
@@ -109,10 +109,19 @@ pipeline {
stage('Upload') {
steps { script {
sshagent(credentials: ['nix-cache-ssh']) {
nix.shell("""
nix.shell(
"""
find /nix/store/ -mindepth 1 -maxdepth 1 -type d \
-not -name '*.links' -and -not -name '*-status-mobile-*' \
| xargs nix copy --to ${NIX_SSH_REMOTE}
-not -name "*.links" -and -not -name "*-status-mobile-*" \
-and -not -name "tmp-*" \
-print0 | xargs -0 nix-store -qR | sort -u > "${WORKSPACE_TMP}"/store-paths.txt
""",
pure: false
)
nix.shell(
"""
nix-store --export < "${WORKSPACE_TMP}"/store-paths.txt | \\
ssh ${env.NIX_SSHOPTS} ${params.NIX_CACHE_USER}@${params.NIX_CACHE_HOST} ${env.NIX_STORE_CMD} --import
""",
pure: false
)
@@ -0,0 +1,13 @@
(ns quo.components.common.new-feature-dot
(:require [quo.components.common.new-feature-gradient :as new-feature-gradient]))
(def ^:const size 8)
(defn view
[{:keys [style accessibility-label]}]
[new-feature-gradient/view
{:style (merge {:width size
:height size
:border-radius (/ size 2)}
style)
:accessibility-label accessibility-label}])
@@ -0,0 +1,16 @@
(ns quo.components.common.new-feature-gradient
(:require [quo.foundations.colors :as colors]
[quo.theme]
[react-native.linear-gradient :as linear-gradient]))
(defn view
[{:keys [style accessibility-label]}]
(let [theme (quo.theme/use-theme)]
[linear-gradient/linear-gradient
{:style style
:accessibility-label accessibility-label
:colors [(colors/resolve-color :yellow theme)
colors/new-feature-gradient-pink]
:use-angle true
:angle 78
:angle-center {:x 0.5 :y 0.5}}]))
@@ -33,4 +33,12 @@
networks-list])
(h/is-truthy (h/query-by-label-text :network-dropdown))
(h/fire-event :press (h/query-by-label-text :network-dropdown))
(h/was-not-called on-press))))
(h/was-not-called on-press)))
(h/test "Should display new chain indicator"
(h/render [network-dropdown/view
{:state :default
:show-new-chain-indicator? true}
networks-list])
(h/is-truthy (h/query-by-label-text :network-dropdown))
(h/is-truthy (h/query-by-label-text :new-chain-indicator))))
@@ -23,3 +23,7 @@
:opacity (if (= state :disabled) 0.3 1)
:border-color (container-border-color props)
:align-items :center})
(def new-chain-indicator
{:position :absolute
:right 0})
@@ -1,12 +1,13 @@
(ns quo.components.dropdowns.network-dropdown.view
(:require
[quo.components.common.new-feature-dot :as new-feature-dot]
[quo.components.dropdowns.network-dropdown.style :as style]
[quo.components.list-items.preview-list.view :as preview-list]
[quo.theme :as quo.theme]
[react-native.core :as rn]))
(defn view
[{:keys [on-press state] :as props} networks]
[{:keys [on-press state show-new-chain-indicator?] :as props} networks]
(let [theme (quo.theme/use-theme)
[pressed? set-pressed] (rn/use-state false)
on-press-in (rn/use-callback #(set-pressed true))
@@ -18,6 +19,10 @@
:on-press on-press
:on-press-in on-press-in
:on-press-out on-press-out}
(when show-new-chain-indicator?
[new-feature-dot/view
{:style style/new-chain-indicator
:accessibility-label :new-chain-indicator}])
[preview-list/view
{:type :network
:list-size (count networks)
@@ -200,12 +200,15 @@
shown-name]]))
(defn- wallet-networks-center
[{:keys [networks networks-on-press background center-content-container-style]}]
[{:keys [networks networks-on-press show-new-chain-indicator? background
center-content-container-style]}]
[reanimated/view {:style center-content-container-style}
[network-dropdown/view
{:state :default
:on-press networks-on-press
:blur? (= background :blur)} networks]])
{:state :default
:on-press networks-on-press
:blur? (= background :blur)
:show-new-chain-indicator? show-new-chain-indicator?}
networks]])
(defn page-nav
"Props:
@@ -64,3 +64,20 @@
{:margin-top 7
:margin-bottom 2
:margin-left -1})
(def new-feature-tag-container
{:margin-left 6
:border-radius 12
:justify-content :center})
(def new-feature-tag-gradient
{:border-radius 12
:position :absolute
:left 0
:right 0
:top 0
:bottom 0})
(def new-feature-tag-text
{:color colors/white
:margin-horizontal 5})
@@ -4,6 +4,7 @@
[quo.components.avatars.icon-avatar :as icon-avatar]
[quo.components.avatars.user-avatar.view :as user-avatar]
[quo.components.buttons.button.view :as button]
[quo.components.common.new-feature-gradient :as new-feature-gradient]
[quo.components.icon :as icon]
[quo.components.list-items.preview-list.view :as preview-list]
[quo.components.markdown.text :as text]
@@ -115,7 +116,9 @@
nil)])
(defn view
[{:keys [title on-press action-props accessibility-label blur? container-style content] :as props}]
[{:keys [title show-new-feature-tag? on-press action-props accessibility-label blur? container-style
content]
:as props}]
[rn/pressable
{:style (merge style/container container-style)
:on-press (or on-press (:on-change action-props))
@@ -126,10 +129,19 @@
[rn/view {:style (style/left-sub-container props)}
[image-component props]
[rn/view {:style (style/left-container (:image props))}
[text/text
{:weight :medium
:style {:color (when blur? colors/white)}}
title]
[rn/view {:flex-direction :row}
[text/text
{:weight :medium
:style {:color (when blur? colors/white)}}
title]
(when show-new-feature-tag?
[rn/view {:style style/new-feature-tag-container}
[new-feature-gradient/view {:style style/new-feature-tag-gradient}]
[text/text
{:weight :semi-bold
:size :label
:style style/new-feature-tag-text}
(string/upper-case (i18n/label :t/new))]])]
[description-component props]
[tag-component props]]]
[rn/view {:style (style/sub-container (:alignment action-props))}
@@ -88,10 +88,3 @@
(def gradient-end
(assoc gradient-common :right 0))
(defn button-loader
[theme]
{:width 100
:height 22
:border-radius 6
:background-color (loader-color theme)})
@@ -40,7 +40,6 @@
[:on-input-focus {:optional true} [:maybe fn?]]
[:on-token-press {:optional true} [:maybe fn?]]
[:on-max-press {:optional true} [:maybe fn?]]
[:max-loading? {:optional true} [:maybe :boolean]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:container-style {:optional true} [:maybe :map]]]]]
:any])
@@ -53,7 +52,7 @@
[{:keys [type status token value fiat-value show-approval-label? error? network-tag-props
approval-label-props default-value auto-focus? input-disabled? enable-swap?
currency-symbol on-change-text show-keyboard? get-ref
container-style on-swap-press on-token-press on-max-press max-loading? on-input-focus]}]
container-style on-swap-press on-token-press on-max-press on-input-focus]}]
(let [theme (quo.theme/use-theme)
pay? (= type :pay)
disabled? (= status :disabled)
@@ -173,13 +172,11 @@
(when-not loading?
[:<>
(when pay?
(if max-loading?
[rn/view {:style (style/button-loader theme)}]
[rn/pressable {:on-press on-max-press}
[network-tag/view
(assoc network-tag-props
:status
(if error? :error :default))]]))
[rn/pressable {:on-press on-max-press}
[network-tag/view
(assoc network-tag-props
:status
(if error? :error :default))]])
(when fiat-value
[text/text
{:size :paragraph-2
@@ -18,5 +18,6 @@
[:dropdown-on-press {:optional true} [:maybe fn?]]
[:networks {:optional true}
[:maybe [:sequential [:map [:source [:maybe :schema.common/image-source]]]]]]
[:dropdown-state {:optional true} [:maybe [:enum :default :disabled]]]]]]
[:dropdown-state {:optional true} [:maybe [:enum :default :disabled]]]
[:show-new-chain-indicator? {:optional true} [:maybe :boolean]]]]]
:any])
@@ -25,7 +25,7 @@
bars))
(defn- view-info-top
[{:keys [state balance networks dropdown-on-press dropdown-state]}]
[{:keys [state balance networks dropdown-on-press dropdown-state show-new-chain-indicator?]}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style style/container-info-top}
(if (= state :loading)
@@ -36,9 +36,10 @@
:style (style/style-text-heading theme)}
balance])
[network-dropdown/view
{:state (or dropdown-state :default)
:blur? true
:on-press dropdown-on-press}
{:state (or dropdown-state :default)
:blur? true
:on-press dropdown-on-press
:show-new-chain-indicator? show-new-chain-indicator?}
networks]]))
(defn- view-metrics
+2
View File
@@ -315,6 +315,8 @@
networks-short-name
socials))
(def new-feature-gradient-pink "#FF33A3")
(defn hex-string?
[s]
(and (string? s) (string/starts-with? s "#")))
-6
View File
@@ -492,16 +492,10 @@
(def ^:const token-for-fees-symbol "ETH")
;; The 15% buffer accounts for Ethereum's EIP-1559 fee mechanism, where the base fee can increase
;; by up to 12.5% per block in periods of high congestion. The extra 2.5% provides additional
;; safety to prevent failed transactions due to rapid gas price fluctuations.
(def ^:const eth-max-fee-buffer-percent 15)
(def ^:const transaction-status-success "Success")
(def ^:const transaction-status-pending "Pending")
(def ^:const transaction-status-failed "Failed")
(def ^:const eth-send-amount-decimal 4)
(def ^:const min-token-decimals-to-display 6)
(def ^:const swap-proposal-refresh-interval-ms 15000)
+2 -1
View File
@@ -275,7 +275,8 @@
{:events [:chat/unmute-chat-community]}
[{:keys [db]} chat-id]
(let [{:keys [community-id]} (get-in db [:chats chat-id])]
{:db (assoc-in db [:communities community-id :muted] false)}))
(when community-id
{:db (assoc-in db [:communities community-id :muted] false)})))
(rf/defn mute-chat-failed
{:events [:chat/mute-failed]}
@@ -1,6 +1,7 @@
(ns status-im.contexts.wallet.common.account-switcher.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.sheets.account-options.view :as account-options]
[status-im.contexts.wallet.sheets.network-filter.view :as network-filter]
[status-im.contexts.wallet.sheets.select-account.view :as select-account]
@@ -30,25 +31,33 @@
type :no-title}}]
(let [{:keys [color emoji watch-only?]} (rf/sub [:wallet/current-viewing-account])
networks (rf/sub [:wallet/selected-network-details])
sending-collectible? (rf/sub [:wallet/sending-collectible?])]
sending-collectible? (rf/sub [:wallet/sending-collectible?])
show-new-chain-indicator? (rf/sub [:wallet/show-new-chain-indicator?])
on-press-networks (rn/use-callback
(fn []
(rf/dispatch [:wallet/hide-new-chain-indicator])
(rf/dispatch [:show-bottom-sheet
{:content network-filter/view}])))]
[quo/page-nav
{:type type
:icon-name icon-name
:margin-top margin-top
:background :blur
:on-press on-press
:accessibility-label accessibility-label
:networks networks
:align-center? true
:networks-on-press #(rf/dispatch [:show-bottom-sheet {:content network-filter/view}])
:right-side [(when (and (ff/enabled? ::ff/wallet.wallet-connect)
(not watch-only?)
show-dapps-button?)
{:icon-name :i/dapps
:on-press #(rf/dispatch [:navigate-to :screen/wallet.connected-dapps])})
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
:on-press #(on-switcher-press switcher-type params)
:emoji emoji
:type (when watch-only? :watch-only)})]}]))
{:type type
:icon-name icon-name
:margin-top margin-top
:background :blur
:on-press on-press
:accessibility-label accessibility-label
:networks networks
:align-center? true
:networks-on-press on-press-networks
:show-new-chain-indicator? show-new-chain-indicator?
:right-side [(when (and (ff/enabled? ::ff/wallet.wallet-connect)
(not watch-only?)
show-dapps-button?)
{:icon-name :i/dapps
:on-press #(rf/dispatch [:navigate-to
:screen/wallet.connected-dapps])})
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
:on-press #(on-switcher-press switcher-type params)
:emoji emoji
:type (when watch-only? :watch-only)})]}]))
@@ -2,7 +2,6 @@
(:require [quo.core :as quo]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.money :as money]
@@ -79,34 +78,34 @@
(defn token-value-drawer
[token watch-only? entry-point]
(let [token-symbol (:token token)
token-data (rf/sub [:wallet/token-by-symbol-from-first-available-account-with-balance
token-symbol])
selected-account (rf/sub [:wallet/current-viewing-account-address])
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
account-owns-token? (rf/sub [:wallet/current-account-owns-token token-symbol])
network-details (rf/sub [:wallet/network-details])
token-owned? (if selected-account account-owns-token? (seq token-owners))
asset-to-receive (rf/sub [:wallet/token-by-symbol-from-first-available-account-with-balance
(swap-utils/default-asset-to-receive token-symbol)])
unique-owner? (= (count token-owners) 1)
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
:has-balance? (-> (get-in token [:values :fiat-unformatted-value])
money/above-zero?))
(and (not selected-account) unique-owner?)
(assoc :token-symbol token-symbol
:token token-data
:stack-id :wallet-stack)
(let [token-symbol (:token token)
token-data (rf/sub [:wallet/token-by-symbol-from-first-available-account-with-balance
token-symbol])
selected-account (rf/sub [:wallet/current-viewing-account-address])
token-owners (rf/sub [:wallet/operable-addresses-with-token-symbol token-symbol])
testnet-mode? (rf/sub [:profile/test-networks-enabled?])
account-owns-token? (rf/sub [:wallet/current-account-owns-token token-symbol])
network-details (rf/sub [:wallet/network-details])
receive-token-symbol (if (= token-symbol "SNT") "ETH" "SNT")
token-owned? (if selected-account account-owns-token? (seq token-owners))
asset-to-receive (rf/sub [:wallet/token-by-symbol receive-token-symbol])
unique-owner? (= (count token-owners) 1)
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
:has-balance? (-> (get-in token [:values :fiat-unformatted-value])
money/above-zero?))
(and (not selected-account) unique-owner?)
(assoc :token-symbol token-symbol
:token token-data
:stack-id :wallet-stack)
(and (not selected-account) (not unique-owner?))
(assoc :token-symbol token-symbol
:stack-id :wallet-stack))]
(and (not selected-account) (not unique-owner?))
(assoc :token-symbol token-symbol
:stack-id :wallet-stack))]
[quo/action-drawer
[(cond->> [(when (ff/enabled? ::ff/wallet.assets-modal-manage-tokens)
(action-manage-tokens watch-only?))
+14 -47
View File
@@ -182,22 +182,12 @@
[amount display-decimals]
(let [number (or (money/bignumber amount)
(money/bignumber 0))
amount-fixed-decimals (-> number
(number/format-decimal-fixed display-decimals)
(number/remove-trailing-zeroes))]
amount-fixed-decimals (number/to-fixed number display-decimals)]
(if (and (= amount-fixed-decimals "0")
(money/above-zero? amount))
(number/small-number-threshold display-decimals)
(str amount-fixed-decimals))))
(defn token-balance-for-network
"Returns the token balance for a specific chain"
[token chain-id]
(let [token-decimals (:decimals token)]
(-> (get-in token [:balances-per-chain chain-id :raw-balance] 0)
(number/convert-to-whole-number token-decimals)
money/bignumber)))
(defn token-balance-display-for-network
"Formats a token balance for a specific chain and rounds it to a specified number of decimals.
If the balance is less than the smallest representable value based on rounding decimals,
@@ -334,20 +324,19 @@
(defn make-network-item
"This function generates props for quo/category component item"
[{:keys [network-name color on-change networks state label-props type blur?]}]
(cond-> {:title (string/capitalize (name network-name))
:image :icon-avatar
:image-props {:icon (resources/get-network network-name)
:size :size-20}
:action :selector
:action-props {:type (or type
(if (= :default state)
:filled-checkbox
:checkbox))
:blur? blur?
:customization-color color
:checked? (contains? networks network-name)
:on-change on-change}}
[{:keys [network-name color on-change networks label-props type blur?]}]
(cond-> {:title (string/capitalize (name network-name))
:image :icon-avatar
:image-props {:icon (resources/get-network network-name)
:size :size-20}
;; Remove the following line for v2.35
:show-new-feature-tag? (= network-name constants/base-network-name)
:action :selector
:action-props {:type (or type :checkbox)
:blur? blur?
:customization-color color
:checked? (contains? networks network-name)
:on-change on-change}}
label-props
(assoc :label :text
@@ -490,25 +479,3 @@
(filter (fn [{:keys [tokens]}]
(some positive-balance-in-any-chain? tokens))
operable-account))))
(defn calculate-max-safe-send-amount
"Calculates the max ETH that can be sent while reserving enough for gas fees.
- Ensures a minimum of 0.0001 ETH and a max of 0.01 ETH for gas.
- Uses 20% of the value as an estimated fee, clamped within this range.
- In Desktop it's 10% but after some more test, we found 20% is better option.
- Prevents sending the full balance to avoid transaction failures.
Aligned with the desktop logic for consistency.
https://github.com/status-im/status-desktop/blob/f320abb5c498ac260a1a4a9db046485b88af81e7/ui/app/AppLayouts/Wallet/WalletUtils.qml#L44"
[value]
(if (or (nil? value) (zero? value))
"0"
(let [raw-fee (money/mul (money/bignumber value) 0.2)
clamped-fee (money/maximum (money/bignumber 0.0001)
(money/minimum (money/bignumber 0.01) raw-fee))
result (money/sub (money/bignumber value) clamped-fee)]
(-> result
(money/maximum 0)
(number/format-decimal-fixed constants/eth-send-amount-decimal)
(number/remove-trailing-zeroes)))))
@@ -202,7 +202,7 @@
(is (= (utils/sanitized-token-amount-to-display 0.0001 3) "<0.001"))
(is (= (utils/sanitized-token-amount-to-display 0.00001 3) "<0.001"))
(is (= (utils/sanitized-token-amount-to-display 0 2) "0"))
(is (= (utils/sanitized-token-amount-to-display 123.456789 4) "123.4567"))
(is (= (utils/sanitized-token-amount-to-display 123.456789 4) "123.4568"))
(is (= (utils/sanitized-token-amount-to-display 0.00000123 6) "0.000001"))
(is (= (utils/sanitized-token-amount-to-display nil 2) "0"))
(is (= (utils/sanitized-token-amount-to-display "invalid" 2) "0"))))
@@ -375,14 +375,3 @@
{:symbol "DAI"}
{:symbol "ETH"}]]
(is (= (utils/sort-tokens-by-name tokens) expected)))))
(deftest calculate-max-safe-send-amount-test
(testing "Calculates the max ETH sendable while reserving fees"
(is (= "0" (utils/calculate-max-safe-send-amount nil)))
(is (= "0" (utils/calculate-max-safe-send-amount 0)))
(is (= "0" (utils/calculate-max-safe-send-amount 0.00009)))
(is (= "0" (utils/calculate-max-safe-send-amount 0.0001)))
(is (= "0.008" (utils/calculate-max-safe-send-amount 0.01)))
(is (= "0.99" (utils/calculate-max-safe-send-amount 1.0)))
(is (= "9.99" (utils/calculate-max-safe-send-amount 10.0)))
(is (= "99.99" (utils/calculate-max-safe-send-amount 100.0)))))
@@ -313,40 +313,3 @@
(-> collectible
transforms/json->clj
transform-collectible))
(defn- bridge-amount-greater-than-bonder-fees?
[{{token-decimals :decimals} :from-token
bonder-fees :tx-bonder-fees
amount-in :amount-in}]
(let [bonder-fees (utils.money/token->unit bonder-fees token-decimals)
amount-to-bridge (utils.money/token->unit amount-in token-decimals)]
(> amount-to-bridge bonder-fees)))
(defn- remove-multichain-routes
[routes]
(if (> (count routes) 1)
[] ;; if route is multichain, we remove it
routes))
(defn- remove-invalid-bonder-fees-routes
[routes]
(filter bridge-amount-greater-than-bonder-fees? routes))
(defn- ->old-route-paths
[routes]
(map new->old-route-path routes))
(def ^:private best-routes-fix
(comp ->old-route-paths
remove-invalid-bonder-fees-routes
remove-multichain-routes))
(def ^:private candidates-fix
(comp ->old-route-paths remove-invalid-bonder-fees-routes))
(defn fix-routes
[data]
(-> data
(rpc->suggested-routes)
(update :best best-routes-fix)
(update :candidates candidates-fix)))
@@ -3,6 +3,7 @@
[clojure.string :as string]
[native-module.core :as native-module]
[promesa.core :as promesa]
[react-native.async-storage :as async-storage]
[status-im.common.json-rpc.events :as json-rpc]
[status-im.contexts.profile.recover.effects :as profile.recover.effects]
[status-im.contexts.wallet.rpc :as wallet-rpc]
@@ -130,3 +131,14 @@
(-> (wallet-rpc/sign-message message address (security/safe-unmask-data password))
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet/retrieve-base-chain-indicator-shown
(fn []
(async-storage/get-item :base-chain-indicator-shown
#(rf/dispatch [:wallet/retrieve-new-chain-indicator (not %)]))))
(rf/reg-fx
:effects.wallet/set-base-chain-indicator-shown
(fn [flag]
(async-storage/set-item! :base-chain-indicator-shown flag)))
+11
View File
@@ -797,3 +797,14 @@
:wallet.swap/transaction-success
:wallet/transaction-success)
sent-transactions])]]})))
(rf/reg-event-fx
:wallet/retrieve-new-chain-indicator
(fn [{:keys [db]} [flag]]
{:db (assoc-in db [:wallet :ui :show-new-chain-indicator?] flag)}))
(rf/reg-event-fx
:wallet/hide-new-chain-indicator
(fn [{:keys [db]}]
{:db (assoc-in db [:wallet :ui :show-new-chain-indicator?] false)
:fx [[:effects.wallet/set-base-chain-indicator-shown true]]}))
+15 -8
View File
@@ -135,7 +135,13 @@
cards (conj account-cards-data (new-account-card-data))
[init-loaded? set-init-loaded] (rn/use-state false)
{:keys [formatted-balance]} (rf/sub [:wallet/aggregated-token-values-and-balance])
theme (quo.theme/use-theme)]
theme (quo.theme/use-theme)
show-new-chain-indicator? (rf/sub [:wallet/show-new-chain-indicator?])
on-press-network-selector (rn/use-callback
(fn []
(rf/dispatch [:wallet/hide-new-chain-indicator])
(rf/dispatch [:show-bottom-sheet
{:content network-filter/view}])))]
(rn/use-effect (fn []
(when (and @account-list-ref (pos? (count cards)))
(.scrollToOffset ^js @account-list-ref
@@ -157,13 +163,14 @@
:on-refresh #(rf/dispatch [:wallet/get-accounts])}]
:header [rn/view {:style (style/header-container theme)}
[quo/wallet-overview
{:state (if tokens-loading? :loading :default)
:time-frame :none
:metrics :none
:balance formatted-balance
:networks networks
:dropdown-on-press #(rf/dispatch [:show-bottom-sheet
{:content network-filter/view}])}]
{:state (if tokens-loading? :loading :default)
:time-frame :none
:metrics :none
:balance formatted-balance
:networks networks
:dropdown-on-press on-press-network-selector
:show-new-chain-indicator? (when (not-empty networks)
show-new-chain-indicator?)}]
(when (ff/enabled? ::ff/wallet.graph)
[quo/wallet-graph {:time-frame :empty}])
[render-cards cards account-list-ref]
+40 -2
View File
@@ -12,6 +12,7 @@
[taoensso.timbre :as log]
[utils.address]
[utils.i18n :as i18n]
[utils.money :as utils.money]
[utils.number]
[utils.re-frame :as rf]
[utils.security.core :as security]))
@@ -622,6 +623,43 @@
{:event :wallet/stop-get-suggested-routes
:error error}))}]]]}))
(defn- bridge-amount-greater-than-bonder-fees?
[{{token-decimals :decimals} :from-token
bonder-fees :tx-bonder-fees
amount-in :amount-in}]
(let [bonder-fees (utils.money/token->unit bonder-fees token-decimals)
amount-to-bridge (utils.money/token->unit amount-in token-decimals)]
(> amount-to-bridge bonder-fees)))
(defn- remove-multichain-routes
[routes]
(if (> (count routes) 1)
[] ;; if route is multichain, we remove it
routes))
(defn- remove-invalid-bonder-fees-routes
[routes]
(filter bridge-amount-greater-than-bonder-fees? routes))
(defn- ->old-route-paths
[routes]
(map data-store/new->old-route-path routes))
(def ^:private best-routes-fix
(comp ->old-route-paths
remove-invalid-bonder-fees-routes
remove-multichain-routes))
(def ^:private candidates-fix
(comp ->old-route-paths remove-invalid-bonder-fees-routes))
(defn- fix-routes
[data]
(-> data
(data-store/rpc->suggested-routes)
(update :best best-routes-fix)
(update :candidates candidates-fix)))
(rf/reg-event-fx
:wallet/handle-suggested-routes
(fn [{:keys [db]} [data]]
@@ -647,8 +685,8 @@
(cond
(and failure? swap?) [:wallet/swap-proposal-error error]
failure? [:wallet/suggested-routes-error error-message]
swap? [:wallet/swap-proposal-success (data-store/fix-routes data)]
:else [:wallet/suggested-routes-success (data-store/fix-routes data)
swap? [:wallet/swap-proposal-success (fix-routes data)]
:else [:wallet/suggested-routes-success (fix-routes data)
enough-assets?])]]}))))))
(rf/reg-event-fx
@@ -18,6 +18,7 @@
:network-name :mainnet
:chain-id 1
:related-chain-id 5}]
:wallet/show-new-chain-indicator? false
:wallet/current-viewing-account-address "0x1"
:wallet/current-viewing-account {:path "m/44'/60'/0'/0/1"
:emoji "💎"
@@ -8,41 +8,26 @@
(defn view
[]
(let [selected-networks (rf/sub [:wallet/selected-networks])
selector-state (rf/sub [:wallet/network-filter-selector-state])
color (rf/sub [:profile/customization-color])
network-details (rf/sub [:wallet/network-details])
viewing-account? (rf/sub [:wallet/viewing-account?])
balance-per-chain (if viewing-account?
(rf/sub [:wallet/current-viewing-account-fiat-balance-per-chain])
(rf/sub [:wallet/aggregated-fiat-balance-per-chain]))
mainnet (first network-details)
layer-2-networks (rest network-details)]
network-details (rf/sub [:wallet/network-details])]
[:<>
[quo/drawer-top {:title (i18n/label :t/select-networks)}]
[quo/information-box
{:type :informative
:icon :i/info
:blur? false
:style {:margin-horizontal 20
:margin-bottom 4}}
(i18n/label :t/base-chain-available-info)]
[quo/category
{:list-type :settings
:data [(utils/make-network-item
{:state selector-state
:network-name (:network-name mainnet)
:color color
:networks selected-networks
:label-props (get balance-per-chain (:chain-id mainnet))
:on-change #(rf/dispatch
[:wallet/update-selected-networks
(:network-name
mainnet)])})]}]
[quo/category
{:list-type :settings
:label (i18n/label :t/layer-2)
:data (mapv (fn [network]
(utils/make-network-item
{:state selector-state
:network-name (:network-name network)
{:network-name (:network-name network)
:color color
:networks selected-networks
:label-props (get balance-per-chain (:chain-id network))
:on-change #(rf/dispatch
[:wallet/update-selected-networks
(:network-name
network)])}))
layer-2-networks)}]]))
network-details)}]]))
+3 -92
View File
@@ -2,7 +2,6 @@
(:require [re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[status-im.contexts.wallet.swap.utils :as swap-utils]
@@ -29,15 +28,6 @@
:account account
:test-networks-enabled? test-networks-enabled?
:token-symbol (get-in data [:asset-to-pay :symbol])}))
received-asset (if-not (nil? asset-to-receive)
asset-to-receive
(swap-utils/select-asset-to-pay-by-symbol
{:wallet wallet
:account account
:test-networks-enabled? test-networks-enabled?
:token-symbol (if (= (:symbol asset-to-pay) "SNT")
"ETH"
"SNT")}))
multi-account-balance? (-> available-accounts
(count)
(> 1))
@@ -46,7 +36,7 @@
start-point (if open-new-screen? :action-menu :swap-button)]
{:db (-> db
(assoc-in [:wallet :ui :swap :asset-to-pay] asset-to-pay)
(assoc-in [:wallet :ui :swap :asset-to-receive] received-asset)
(assoc-in [:wallet :ui :swap :asset-to-receive] asset-to-receive)
(assoc-in [:wallet :ui :swap :network] network')
(assoc-in [:wallet :ui :swap :launch-screen] view-id)
(assoc-in [:wallet :ui :swap :start-point] start-point))
@@ -63,7 +53,7 @@
[:centralized-metrics/track :metric/swap-start
{:network (:chain-id network)
:pay_token (:symbol asset-to-pay)
:receive_token (:symbol received-asset)
:receive_token (:symbol asset-to-receive)
:start_point start-point
:launch_screen view-id}]]
[:dispatch [:wallet.swap/set-default-slippage]]]
@@ -77,7 +67,7 @@
(rf/dispatch
[:wallet.swap/start
{:asset-to-pay asset-to-pay
:asset-to-receive received-asset
:asset-to-receive asset-to-receive
:network network
:open-new-screen?
open-new-screen?
@@ -587,82 +577,3 @@
[:dispatch
[:navigate-to-within-stack
[:screen/wallet.swap-select-asset-to-pay :screen/wallet.swap-select-account]]]])})))
(rf/reg-event-fx :wallet/get-swap-proposal-fee
(fn [{:keys [db]} [{:keys [amount-in amount-out]}]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
{:keys [asset-to-pay asset-to-receive
network]} (get-in db [:wallet :ui :swap])
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks ((if test-networks-enabled? :test :prod)
(get-in db [:wallet :networks]))
network-chain-ids (map :chain-id networks)
pay-token-decimal (:decimals asset-to-pay)
pay-token-id (:symbol asset-to-pay)
receive-token-id (:symbol asset-to-receive)
receive-token-decimals (:decimals asset-to-receive)
gas-rates constants/gas-rate-medium
amount-in-hex (if amount-in
(send-utils/amount-in-hex amount-in pay-token-decimal)
0)
amount-out-hex (when amount-out
(send-utils/amount-in-hex amount-out receive-token-decimals))
to-address wallet-address
from-address wallet-address
swap-chain-id (:chain-id network)
disabled-to-chain-ids (filter #(not= % swap-chain-id) network-chain-ids)
disabled-from-chain-ids (filter #(not= % swap-chain-id) network-chain-ids)
from-locked-amount {}
send-type constants/send-type-swap
request-uuid (str (random-uuid))
params [(cond->
{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
:tokenID pay-token-id
:toTokenID receive-token-id
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount from-locked-amount
:amountOut (or amount-out-hex "0x0")}
amount-in (assoc :amountIn amount-in-hex))]]
{:db (assoc-in db [:wallet :ui :swap :loading-swap-proposal-fee?] true)
:json-rpc/call [{:method "wallet_getSuggestedRoutes"
:params params
:on-success (fn [data]
(let [swap-proposal (data-store/fix-routes data)]
(rf/dispatch [:wallet/swap-proposal-fee-success
swap-proposal])))
:on-error (fn [error]
(rf/dispatch [:wallet/swap-proposal-fee-error])
(log/error "failed to get suggested routes"
{:event :wallet/get-swap-proposal-fee
:error (:message error)
:params params}))}]})))
(rf/reg-event-fx
:wallet/swap-proposal-fee-success
(fn [{:keys [db]} [swap-proposal]]
(let [best-routes (:best swap-proposal)
selected-route (first best-routes)
relevant-fee-fields [:gas-amount :gas-fees :token-fees :approval-required
:approval-fee :approval-l-1-fee :bonder-fees]
fee-data (select-keys selected-route relevant-fee-fields)]
{:db (update-in db
[:wallet :ui :swap]
assoc
:loading-swap-proposal-fee? false
:swap-proposal
(when-not (empty? best-routes)
fee-data))})))
(rf/reg-event-fx
:wallet/swap-proposal-fee-error
(fn [{:keys [db]}]
{:db (update-in db
[:wallet :ui :swap]
assoc
:loading-swap-proposal-fee?
false)}))
@@ -5,7 +5,6 @@
[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.swap.select-asset-to-pay.style :as style]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -21,11 +20,11 @@
(defn- assets-view
[search-text on-change-text]
(let [on-token-press (fn [token]
(let [snt-token (rf/sub [:wallet/token-by-symbol "SNT"])
eth-token (rf/sub [:wallet/token-by-symbol "ETH"])
on-token-press (fn [token]
(let [pay-token-symbol (:symbol token)
asset-to-receive (rf/sub [:wallet/token-by-symbol
(swap-utils/default-asset-to-receive
pay-token-symbol)])]
asset-to-receive (if (= pay-token-symbol "SNT") eth-token snt-token)]
(rf/dispatch [:wallet.swap/start
{:asset-to-pay {:symbol pay-token-symbol}
:asset-to-receive asset-to-receive
@@ -115,7 +115,6 @@
pay-token-symbol (rf/sub [:wallet/swap-asset-to-pay-symbol])
pay-token-decimals (rf/sub [:wallet/swap-asset-to-pay-decimals])
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
loading-swap-proposal-fee? (rf/sub [:wallet/swap-loading-swap-proposal-fee?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
approval-required (rf/sub [:wallet/swap-proposal-approval-required])
approval-amount-required (rf/sub [:wallet/swap-proposal-approval-amount-required])
@@ -123,7 +122,6 @@
approval-transaction-id (rf/sub [:wallet/swap-approval-transaction-id])
approved-amount (rf/sub [:wallet/swap-approved-amount])
error-response (rf/sub [:wallet/swap-error-response])
eth-proposal? (= pay-token-symbol "ETH")
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)))
@@ -133,16 +131,12 @@
(:chain-id network)])
pay-token-fiat-value (rf/sub [:wallet/swap-asset-to-pay-amount-in-fiat
pay-input-num-value])
available-crypto-limit (rf/sub [:wallet/swap-available-crypto-limit])
display-decimals (min pay-token-decimals
(if eth-proposal?
constants/eth-send-amount-decimal
constants/min-token-decimals-to-display))
total-crypto-limit (money/bignumber
available-crypto-limit (money/bignumber
pay-token-balance-selected-chain)
total-crypto-limit-display (utils/sanitized-token-amount-to-display
total-crypto-limit
display-decimals)
display-decimals (min pay-token-decimals
constants/min-token-decimals-to-display)
available-crypto-limit-display (rf/sub [:wallet/swap-asset-to-pay-balance-for-chain-ui
(:chain-id network)])
approval-amount-required-num (when approval-amount-required
(number/to-fixed (number/hex->whole
approval-amount-required
@@ -163,36 +157,14 @@
pay-input-amount))
(> pay-input-amount 0)
(not pay-input-error?))
request-swap-proposal-fee (rn/use-callback
(fn []
(let [safe-send-amount (utils/calculate-max-safe-send-amount
pay-token-balance-selected-chain)]
(rf/dispatch [:wallet/get-swap-proposal-fee
{:amount-in safe-send-amount}])))
[pay-token-balance-selected-chain])
request-fetch-swap-proposal (rn/use-callback
(fn []
(fetch-swap-proposal
{:amount pay-input-amount
:valid-input? valid-pay-input?
:clean-approval-transaction? true}))
[pay-input-amount])
on-max-balance-press (rn/use-callback
(fn []
(let [max-value (if eth-proposal?
(number/format-decimal-fixed
available-crypto-limit
constants/eth-send-amount-decimal)
(money/to-string available-crypto-limit))]
(when (money/greater-than max-value 0)
(on-max-press max-value))))
[available-crypto-limit eth-proposal?])]
[pay-input-amount])]
(rn/use-unmount #(rf/dispatch [:wallet/clean-swap]))
(rn/use-effect
(fn []
(when eth-proposal?
(request-swap-proposal-fee)))
[eth-proposal?])
(rn/use-effect
(fn []
(request-fetch-swap-proposal))
@@ -224,13 +196,12 @@
input-focused? :typing
:else :disabled)
:on-token-press on-token-press
:on-max-press on-max-balance-press
:max-loading? loading-swap-proposal-fee?
:on-max-press #(on-max-press (str pay-token-balance-selected-chain))
:on-input-focus on-input-focus
:value pay-input-amount
:fiat-value pay-token-fiat-value
:network-tag-props {:title (i18n/label :t/max-token
{:number total-crypto-limit-display
{:number available-crypto-limit-display
:token-symbol pay-token-symbol})
:networks [{:source (:source network)}]}
:approval-label-props {:status (case approval-transaction-status
@@ -331,16 +302,16 @@
(defn- action-button
[{:keys [on-press]}]
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
swap-proposal-received-amount (rf/sub [:wallet/swap-proposal-amount-out])
error-response (rf/sub [:wallet/swap-error-response])
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
approval-required? (rf/sub [:wallet/swap-proposal-approval-required])
approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status])]
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
error-response (rf/sub [:wallet/swap-error-response])
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
approval-required? (rf/sub [:wallet/swap-proposal-approval-required])
approval-transaction-status (rf/sub [:wallet/swap-approval-transaction-status])]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/review-swap)
:button-one-props {:disabled? (or (not swap-proposal-received-amount)
:button-one-props {:disabled? (or (not swap-proposal)
error-response
(and approval-required?
(not= approval-transaction-status :confirmed))
@@ -62,9 +62,3 @@
[{:keys [networks]}]
(when (= (count networks) 1)
(first networks)))
(defn default-asset-to-receive
[pay-token-symbol]
(cond (= pay-token-symbol "SNT") "ETH"
(= pay-token-symbol "ETH") "USDC"
:else "SNT"))
+3 -1
View File
@@ -78,4 +78,6 @@
;;app starting flow continues in get-profiles-overview
:profile/get-profiles-overview #(rf/dispatch [:profile/get-profiles-overview-success %])
:effects.font/get-font-file-for-initials-avatar
#(rf/dispatch [:font/init-font-file-for-initials-avatar %])}))
#(rf/dispatch [:font/init-font-file-for-initials-avatar %])
;; show new chain indicator to highlight base chain. This will be be removed in v2.35.
:effects.wallet/retrieve-base-chain-indicator-shown nil}))
-30
View File
@@ -142,11 +142,6 @@
:<- [:wallet/swap]
:-> :loading-swap-proposal?)
(rf/reg-sub
:wallet/swap-loading-swap-proposal-fee?
:<- [:wallet/swap]
:-> :loading-swap-proposal-fee?)
(rf/reg-sub
:wallet/swap-transaction-for-signing
:<- [:wallet/swap]
@@ -378,28 +373,3 @@
:token asset-to-pay
:prices-per-token prices-per-token})]
(utils/fiat-formatted-for-ui currency-symbol fiat-value)))))
(rf/reg-sub :wallet/max-swap-fee
:<- [:wallet/swap-proposal-approval-required]
:<- [:wallet/swap-approval-fee]
:<- [:wallet/swap-proposal]
(fn [[approval-required swap-approval-fee swap-proposal]]
(let [wallet-swap-proposal-fee (send-utils/full-route-gas-fee [swap-proposal])]
(if approval-required
(money/add swap-approval-fee wallet-swap-proposal-fee)
wallet-swap-proposal-fee))))
(rf/reg-sub :wallet/swap-available-crypto-limit
:<- [:wallet/swap-network]
:<- [:wallet/swap-asset-to-pay-balance-for-chain-data]
:<- [:wallet/max-swap-fee]
:<- [:wallet/swap-asset-to-pay-symbol]
(fn [[network asset-to-pay-with-current-account-balance max-swap-fee pay-token-symbol]]
(let [pay-token-balance (utils/token-balance-for-network
asset-to-pay-with-current-account-balance
(:chain-id network))]
(if (= pay-token-symbol constants/token-for-fees-symbol)
(let [buffered-fee (money/mul max-swap-fee
(inc (/ constants/eth-max-fee-buffer-percent 100)))]
(money/sub pay-token-balance buffered-fee))
pay-token-balance))))
+5
View File
@@ -47,6 +47,11 @@
:<- [:wallet/ui]
:-> :last-updates-per-address)
(rf/reg-sub
:wallet/show-new-chain-indicator?
:<- [:wallet/ui]
:-> :show-new-chain-indicator?)
(rf/reg-sub
:wallet/latest-update
:<- [:wallet/last-updates-per-address]
-10
View File
@@ -246,13 +246,3 @@
(schema/=> format-amount
[:=> [:cat [:maybe :int]]
[:maybe :string]])
(defn maximum
[n1 n2]
(when-let [[^js bn1 ^js bn2] (->bignumbers n1 n2)]
(if (greater-than-or-equals bn1 bn2) bn1 bn2)))
(defn minimum
[n1 n2]
(when-let [[^js bn1 ^js bn2] (->bignumbers n1 n2)]
(if (less-than bn1 bn2) bn1 bn2)))
-15
View File
@@ -97,18 +97,3 @@
[num decimal-count]
(let [decimal-part (second (string/split (str num) #"\."))]
(or (nil? decimal-part) (<= (count decimal-part) decimal-count))))
(defn format-decimal-fixed
"Formats a number `n` to `decimal-places` without rounding.
- Ensures the exact number of decimal places (including trailing zeros).
- Does not round up or down, just trims excess decimals."
[n decimal-places]
(let [bn (money/bignumber n)
scale (money/bignumber (money/from-decimal decimal-places))
truncated (if (money/less-than bn 0)
(Math/ceil (money/mul bn scale))
(Math/floor (money/mul bn scale)))]
(-> truncated
(money/bignumber)
(money/div scale)
(money/to-fixed decimal-places))))
-16
View File
@@ -88,19 +88,3 @@
(is (true? (utils.number/valid-decimal-count? 1234567890.12 2)))
(is (false? (utils.number/valid-decimal-count? 1234567890.12345 3)))))
(deftest format-decimal-fixed-test
(testing "Format decimal numbers correctly without rounding"
(is (= "123.45" (utils.number/format-decimal-fixed "123.456" 2)))
(is (= "123.456" (utils.number/format-decimal-fixed "123.456" 3)))
(is (= "123.45600" (utils.number/format-decimal-fixed "123.456" 5)))
(is (= "123.000" (utils.number/format-decimal-fixed "123.000" 3)))
(is (= "123" (utils.number/format-decimal-fixed "123.999" 0)))
(is (= "-123.4" (utils.number/format-decimal-fixed "-123.456" 1)))
(is (= "-123.45" (utils.number/format-decimal-fixed "-123.456" 2)))
(is (= "0.0006" (utils.number/format-decimal-fixed "0.000650462672354754" 4)))
(is (= "999999999.999" (utils.number/format-decimal-fixed "999999999.999999" 3)))))
@@ -41,7 +41,7 @@ class TestWalletCollectibles(MultipleSharedDeviceTestCase):
@marks.testrail_id(741839)
def test_wallet_collectibles_balance(self):
self.wallet_view.collectibles_tab.click()
self.wallet_view.set_network_in_wallet(self.network_name)
self.wallet_view.set_network_in_wallet(self.network_name + ', NEW')
collectibles = {
"BVL": {"quantity": 2,
"info": {"Account": "Account 1",
@@ -52,7 +52,7 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
'Mainnet': self.mainnet_balance,
'Arbitrum': self.arb_balance,
'Optimism': self.optimism_balance,
'Base': self.base_balance
'Base, NEW': self.base_balance
}
for network in expected_balances:
+1 -1
View File
@@ -182,7 +182,7 @@ class WalletView(BaseView):
def set_network_in_wallet(self, network_name: str):
self.network_drop_down.click()
Button(self.driver, accessibility_id="%s, label-component" % network_name.capitalize()).click()
Button(self.driver, accessibility_id="%s, label-component" % network_name).click()
self.network_drop_down.click()
def get_account_element(self, account_name: str = 'Account 1'):
+1
View File
@@ -200,6 +200,7 @@
"bad-fees-description": "Your priority fee is below our suggested parameters.",
"balance": "Balance",
"base": "Base",
"base-chain-available-info": "Base chain is now on Status - view your assets, interact with dApps and swap tokens",
"be-safe-with-secure-cold-wallet": "Be safe with secure cold wallet",
"begin-set-up": "Begin setup",
"below-base-fee": "max fee below base fee",