Compare commits

...
Author SHA1 Message Date
Ulises M b6ab9caf39 lint fix 2024-10-17 23:33:23 -06:00
Ulises M 7ffa680761 set fixed bottom as designs 2024-10-17 23:31:05 -06:00
Ulises M d6041c90a4 WIP 2024-10-17 23:31:04 -06:00
Ulises M 9e9833e732 WIP: fix spacing and code style 2024-10-17 23:31:04 -06:00
frank dd0ba49bea fix_: message history loading took too much time (#21411)
https://github.com/status-im/status-go/compare/12c4e869...43659f0c
2024-10-18 10:51:27 +08:00
Mohsen 6105435207 [#20593] fix: show name of saved address in sending flow (#21448) 2024-10-17 19:47:07 +03:00
Ulises Manuel 711fd19967 fix(wallet): "Not enough assets" case in send screen (#21425)
* Make `->bignumbers` more general

* Fix case when there are not enough assets to pay gas fees:
  - Fix the UI to match Figma
  - Fix logic to handle the case
  - Perform refactoring to surrounding code
2024-10-16 13:45:21 -06:00
Brian Sztamfater d58553f67a fix(swap): update swap icon and don't show contract address for eth swaps (#21410)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-16 12:51:29 -03:00
Tetiana Churikova 76882690d7 e2e: fix error in reporting (#21447) 2024-10-16 15:27:00 +02:00
Tetiana Churikova 6a33c4723a e2e: balance test (#21440) 2024-10-16 11:26:57 +02:00
Lungu Cristian 6a26ebafa3 fix: clean up state after unmounting (#21435) 2024-10-16 13:27:01 +05:30
Mohsen 3c55fc1fa2 [#21335] feat: add dApp analytics (#21379) 2024-10-15 20:22:05 +03:00
flexsurfer 15a1a15c08 Keycard - Allow user to migrate existing Profile [Part 2] (#21408) 2024-10-15 16:48:47 +02:00
Mohsen 9e43aa04c7 [#21396] chore: rename crypto buy phrases (#21404) 2024-10-15 14:15:07 +03:00
flexsurfer 18f7d05a61 Feature/keycard create pin #21368 (#21413) 2024-10-14 10:33:18 +02:00
frank 70e325fd2b fix_: community sync (#21327)
https://github.com/status-im/status-go/compare/12c4e869...3f613a92
2024-10-12 09:02:33 +08:00
flexsurfer fba631c044 [#21420] Keycard - Auth with password (#21422) 2024-10-11 18:35:39 +02:00
richΛrdandPrem Chaitanya Prathi 598617cf2b chore: fix mismatched staging fleet peerID and take filter loop fix and updates from status-go (#21386)
https://github.com/status-im/status-go/compare/12c4e869...0f4f2f4c

Co-authored-by: Prem Chaitanya Prathi <chaitanyaprem@gmail.com>
2024-10-11 22:07:12 +08:00
Siddarth Kumar 66f177370a nix: fix sha256 of nixpkgs release 24-05 (#21416) 2024-10-11 15:16:25 +05:30
Volodymyr Kozieiev ad7d58788f Missed key added to token returned from assets list (#21402) 2024-10-10 11:55:03 +01:00
Alexander ad4ea0783f A black square background at the end of Swap confirmation screen (#21372) 2024-10-09 15:37:38 -03:00
Brian Sztamfater 5c44cb6399 fix(swap): display very small max values, fix scientific notation, handle decimal mismatch when changing tokens, display long numbers (#21388)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-09 13:49:45 -03:00
Ulises Manuel a9e0b3dd6b fix(wallet): Exception when navigating back from route generation (#21381)
- Slightly improved code style
2024-10-08 19:05:29 -06:00
Ulises Manuel 131da5f444 fix(community): Last message overlapped with "join community" button (#21370) 2024-10-08 18:50:29 -06:00
Brian Sztamfater 874ececc47 fix(swap): update max value when the account is switched (#21395)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-08 18:16:38 -03:00
Yevheniia Berdnyk 5f1d74a77a e2e: added tests for fallback flow 2024-10-08 17:49:27 +03:00
Brian Sztamfater 00b0d75fc4 fix(wallet): mercuryo on-ramp (#20962)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-10-08 09:43:56 -03:00
Siddarth Kumar 4b93c8ff43 nix: use multiple nixpkgs sources (#21374) 2024-10-08 17:51:30 +05:30
flexsurfer 80ad2b8fca [#21318] Keycard - Allow user to initiate Profile key pair migration on an empty Keycard (#21359) 2024-10-07 20:15:19 +02:00
Alexander a8bc93eb17 Account selection drawer with balance tag (#21356) 2024-10-07 14:57:23 -03:00
Parvesh Monu 9d3fc4286e fix syncing screen only shows paired label after the restart (#21315)
https://github.com/status-im/status-go/compare/effde33d...12c4e869
2024-10-07 22:35:46 +08:00
108 changed files with 2218 additions and 968 deletions
-1
View File
@@ -51,7 +51,6 @@ in {
};
# Package version adjustments
gradle = super.gradle_8.override { java = super.openjdk17_headless; };
nodejs = super.nodejs_20;
ruby = super.ruby_3_1;
yarn = super.yarn.override { nodejs = super.nodejs_20; };
+18 -2
View File
@@ -6,8 +6,16 @@ let
# For testing local version of nixpkgs
#nixpkgsSrc = (import <nixpkgs> { }).lib.cleanSource "/home/jakubgs/work/nixpkgs";
# We use a commit from the unstable channel of nixpkgs for gradle 8.8
# We follow release 24-05 of nixpkgs
# https://github.com/NixOS/nixpkgs/releases/tag/24.05
nixpkgsSrc = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/df27247e6f3e636c119e2610bf12d38b5e98cc79.tar.gz";
sha256 = "sha256:0bbvimk7xb7akrx106mmsiwf9nzxnssisqmqffla03zz51d0kz2n";
};
# FIXME: remove this additional source when nixpkgs includes gradle 8.8 in stable channel
# We use a commit from the unstable channel of nixpkgs for gradle 8.8
gradleNixpkgsSrc = builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/48d567fc7b299de90f70a48e4263e31f690ba03e.tar.gz";
sha256 = "sha256:0zynbk52khdfhg4qfv26h3r5156xff5p0cga2cin7b07i7lqminh";
};
@@ -21,6 +29,14 @@ let
# Override some packages and utilities
pkgsOverlay = import ./overlay.nix;
# FIXME: remove this additional source when nixpkgs includes gradle 8.8 in stable channel
gradleOverlay = final: prev: {
gradle = (import gradleNixpkgsSrc {
inherit (prev) system;
config = defaultConfig // config;
}).gradle_8.override { java = prev.openjdk17_headless; };
};
# Fix for lack of Android SDK for M1 Macs.
systemOverride = let
inherit (builtins) currentSystem getEnv;
@@ -35,5 +51,5 @@ in
(import nixpkgsSrc) {
config = defaultConfig // config;
system = systemOverride;
overlays = [ pkgsOverlay ];
overlays = [ pkgsOverlay gradleOverlay ];
}
+3 -3
View File
@@ -180,17 +180,17 @@
},
"shards.staging": {
"tcp/p2p/waku/boot": {
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmEqqio4UR1SWqAc7KY19t6qyDvtmyjreZpzUBJvb4u65R",
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmQE7FXQc6iZHdBzYfw3qCSDa9dLc1wsBJKoP4aZvztq2d",
"boot-01.gc-us-central1-a.shards.staging": "/dns4/boot-01.gc-us-central1-a.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmGAA54bBTE78MYidSy3P7Q9yAWFNTAEReJYD69VRvtL5r",
"boot-01.ac-cn-hongkong-c.shards.staging": "/dns4/boot-01.ac-cn-hongkong-c.shards.staging.status.im/tcp/30303/p2p/16Uiu2HAmNTpGnyZ8W1BK2sXEmgSCNWiyDKgRU3NBR2DXST2HzxRU"
},
"enr/p2p/waku/boot": {
"boot-01.do-ams3.shards.staging": "enr:-QEQuEDsh6FgAb_36cReaX7W4gWx_7_GNpsUki7bXMoMrrrWij5pDEyV3guR-urDW_6GJTAzpQiJV61F-CfNn_NxPbY-AYJpZIJ2NIJpcISPxvrpim11bHRpYWRkcnO4YAAtNihib290LTAxLmRvLWFtczMuc2hhcmRzLnN0YWdpbmcuc3RhdHVzLmltBnZfAC82KGJvb3QtMDEuZG8tYW1zMy5zaGFyZHMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDIH8BcuEzgnmwPQTu7BPYyg4u4om7K9qekKA2gT_H2wSDdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"boot-01.do-ams3.shards.staging": "enr:-QEQuEBuiQgFlJNcv255042zwyl4pOBOivakX8N30Dr9vaaEU2q8-7N4GVY4Hk87iEKELjlIXTpE9Wj6EQq1lrBuc7ayAYJpZIJ2NIJpcISPxvrpim11bHRpYWRkcnO4YAAtNihib290LTAxLmRvLWFtczMuc3RhdHVzLnN0YWdpbmcuc3RhdHVzLmltBnZfAC82KGJvb3QtMDEuZG8tYW1zMy5zdGF0dXMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDq-yGgpuoUG6NKkbIDRmrMiT-bEVzFlpWLEK_rF3yKUaDdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"boot-01.gc-us-central1-a.shards.staging": "enr:-QEiuECWCuk1CoefbPYUV_cff27t-471k_QhJ_MvgOUQmRE4vgI6h9fD4LgXy7TZNSFXeBBP9haWJB7lx8Wujx-LPmhrAYJpZIJ2NIJpcIRoxQVgim11bHRpYWRkcnO4cgA2NjFib290LTAxLmdjLXVzLWNlbnRyYWwxLWEuc2hhcmRzLnN0YWdpbmcuc3RhdHVzLmltBnZfADg2MWJvb3QtMDEuZ2MtdXMtY2VudHJhbDEtYS5zaGFyZHMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDNAvlGjekD1YV4WpmjwArGAH2g9kHFJnMRfgUhcIkoA2DdGNwgnZfg3VkcIIjKIV3YWt1Mg0",
"boot-01.ac-cn-hongkong-c.shards.staging": "enr:-QEiuEAcHWW7aNBtJhigTCAnKu9-H1b98SWpBJkGm3PAxth5QjJA0kdiabOfou4HgCD6RuyZEnFE5L8ymahouw3kWRiWAYJpZIJ2NIJpcIQvTKi6im11bHRpYWRkcnO4cgA2NjFib290LTAxLmFjLWNuLWhvbmdrb25nLWMuc2hhcmRzLnN0YWdpbmcuc3RhdHVzLmltBnZfADg2MWJvb3QtMDEuYWMtY24taG9uZ2tvbmctYy5zaGFyZHMuc3RhZ2luZy5zdGF0dXMuaW0GAbveA4Jyc40AEAUAAQAgAEAAgAEAiXNlY3AyNTZrMaEDkbgV7oqPNmFtX5FzSPi9WH8kkmrPB1R3n9xRXge91M-DdGNwgnZfg3VkcIIjKIV3YWt1Mg0"
},
"wss/p2p/waku/boot": {
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmEqqio4UR1SWqAc7KY19t6qyDvtmyjreZpzUBJvb4u65R",
"boot-01.do-ams3.shards.staging": "/dns4/boot-01.do-ams3.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmQE7FXQc6iZHdBzYfw3qCSDa9dLc1wsBJKoP4aZvztq2d",
"boot-01.gc-us-central1-a.shards.staging": "/dns4/boot-01.gc-us-central1-a.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmGAA54bBTE78MYidSy3P7Q9yAWFNTAEReJYD69VRvtL5r",
"boot-01.ac-cn-hongkong-c.shards.staging": "/dns4/boot-01.ac-cn-hongkong-c.shards.staging.status.im/tcp/443/wss/p2p/16Uiu2HAmNTpGnyZ8W1BK2sXEmgSCNWiyDKgRU3NBR2DXST2HzxRU"
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+4 -3
View File
@@ -256,9 +256,10 @@
{:events [:pairing/pair-and-sync]}
[cofx installation-id]
{:fx [[:json-rpc/call
[{:method "wakuext_enableInstallationAndSync"
:params [{:installationId installation-id}]
:on-success #(log/debug "successfully synced devices")}]]]})
[{:method "wakuext_enableInstallationAndSync"
:params [{:installationId installation-id}]
:js-response true
:on-success [:sanitize-messages-and-process-response]}]]]})
(rf/defn enable-installation-success
{:events [:pairing.callback/enable-installation-success]}
@@ -48,7 +48,7 @@
:accessibility-label :receive}]
(when swap-action
[action-button
{:icon :i/swap
{:icon :i/transaction
:text (i18n/label :t/swap)
:on-press swap-action
:theme theme
@@ -65,7 +65,7 @@
:type type
:include-button? include-button?})
style)}
[icons/icon icon
[icons/icon (or icon :i/info)
{:color (style/get-color-by-type theme type :icon)
:no-color no-icon-color?
:size (or icon-size 16)
@@ -1,6 +1,7 @@
(ns quo.components.list-items.network-list.view
(:require
[clojure.string :as string]
[quo.components.icon :as icon]
[quo.components.list-items.network-list.style :as style]
[quo.components.markdown.text :as text]
[quo.theme :as quo.theme]
@@ -42,8 +43,8 @@
[:map {:closed true}
[:network-image :int]
[:label :string]
[:fiat-value :string]
[:token-value :string]
[:fiat-value {:optional true} [:maybe :string]]
[:token-value {:optional true} [:maybe :string]]
[:container-style {:optional true} [:maybe :map]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:state {:optional true} [:enum :pressed :active :disabled :default]]
@@ -51,7 +52,7 @@
:any])
(defn- view-internal
[{:keys [on-press container-style state customization-color]
[{:keys [fiat-value token-value on-press container-style state customization-color]
:as props
:or {customization-color :blue}}]
(let [theme (quo.theme/use-theme)
@@ -67,6 +68,8 @@
:on-press (when-not (= state :disabled) on-press)
:accessibility-label :network-list}
[info props]
[values props]]))
(if (or token-value fiat-value)
[values props]
[icon/icon :i/chevron-right])]))
(def view (schema/instrument #'view-internal ?schema))
+1 -3
View File
@@ -4,12 +4,10 @@
[container-style]
(merge container-style
{:flex-direction :row
:flex 1
:align-items :flex-start}))
(def index
{:margin-left 5})
(def text-container
{:margin-left 8
:flex 1})
{:margin-left 8})
@@ -60,7 +60,8 @@
(defn- view-internal
[{:keys [type account-props token-props networks? values]}]
(let [theme (quo.theme/use-theme)]
(let [theme (quo.theme/use-theme)
address (or (:address account-props) (:address token-props))]
[rn/view
{:style (style/container networks? theme)}
[rn/view
@@ -89,12 +90,13 @@
(get-in account-props [:status-account :name])]
[rn/view
{:style (style/dot-divider theme)}]])
[text/text
{:size (when (not= type :account) :paragraph-2)
:weight (when (= type :account) :semi-bold)
:style {:color (when (not= type :account)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}}
(or (:address account-props) (:address token-props))]]]]
(when address
[text/text
{:size (when (not= type :account) :paragraph-2)
:weight (when (= type :account) :semi-bold)
:style {:color (when (not= type :account)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}}
address])]]]
(when networks?
[:<>
[rn/view
@@ -75,3 +75,16 @@
(def fiat-amount
{:color colors/neutral-50})
(def gradient-common
{:width 64
:position :absolute
:top 0
:bottom 0
:z-index 1})
(def gradient-start
(assoc gradient-common :left 0))
(def gradient-end
(assoc gradient-common :right 0))
+91 -32
View File
@@ -11,6 +11,7 @@
[quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[schema.core :as schema]))
(def ?schema
@@ -18,6 +19,7 @@
[:catn
[:props
[:map {:closed true}
[:get-ref {:optional true} [:maybe fn?]]
[:type {:optional true} [:maybe [:enum :pay :receive]]]
[:status {:optional true} [:maybe [:enum :default :typing :disabled :loading]]]
[:token {:optional true} [:maybe :string]]
@@ -42,26 +44,65 @@
[:container-style {:optional true} [:maybe :map]]]]]
:any])
(def icon-size 32)
(def container-padding (* 2 (:padding (style/row-1 false))))
(def max-cursor-position 5)
(defn view-internal
[{: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?
currency-symbol on-change-text show-keyboard? get-ref
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)
loading? (= status :loading)
typing? (= status :typing)
controlled-input? (some? value)
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref] (reset! input-ref ref)) [])
focus-input (rn/use-callback (fn []
(some-> @input-ref
(oops/ocall "focus")))
[input-ref])]
(let [theme (quo.theme/use-theme)
pay? (= type :pay)
disabled? (= status :disabled)
loading? (= status :loading)
typing? (= status :typing)
controlled-input? (some? value)
[container-width
set-container-width] (rn/use-state)
[overflow?
set-overflow?] (rn/use-state false)
[cursor-close-to-start?
set-cursor-close-to-start?] (rn/use-state false)
[label-width
set-label-width] (rn/use-state 0)
input-ref (rn/use-ref-atom nil)
set-input-ref (rn/use-callback (fn [ref]
(reset! input-ref ref)
(when get-ref (get-ref ref)))
[])
focus-input (rn/use-callback (fn []
(some-> @input-ref
(oops/ocall "focus")))
[input-ref])
on-layout-container (rn/use-callback
(fn [e]
(let [width (oops/oget e "nativeEvent.layout.width")]
(set-container-width width))))
on-layout-text-input (rn/use-callback
(fn [e]
(let [width (oops/oget e "nativeEvent.layout.width")
max-width (- container-width
icon-size
container-padding
(* label-width 2))]
(set-overflow? (> width max-width))))
[container-width label-width])
on-layout-label (rn/use-callback
(fn [e]
(let [width (oops/oget e "nativeEvent.layout.width")]
(set-label-width width))))
on-selection-change (rn/use-callback
(fn [e]
(let [selection-start (oops/oget e
"nativeEvent.selection.start")]
(set-cursor-close-to-start?
(< selection-start max-cursor-position)))))]
[rn/view
{:style container-style
:accessibility-label :swap-input}
:accessibility-label :swap-input
:on-layout on-layout-container}
[rn/view {:style (style/content typing? theme)}
[rn/view
{:style (style/row-1 loading?)}
@@ -75,25 +116,43 @@
[rn/pressable
{:style style/input-container
:on-press focus-input}
[rn/text-input
(cond-> {:ref set-input-ref
:style (style/input disabled? error? theme)
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:keyboard-type :numeric
:editable (not input-disabled?)
:auto-focus auto-focus?
:on-focus on-input-focus
:on-change-text on-change-text
:show-soft-input-on-focus show-keyboard?
:default-value default-value
:placeholder "0"}
controlled-input? (assoc :value value))]
[rn/view {:style {:flex-shrink 1}}
(when (and overflow? typing? (not cursor-close-to-start?))
[linear-gradient/linear-gradient
{:start {:x 0 :y 0}
:end {:x 1 :y 0}
:colors [(colors/theme-colors colors/white colors/neutral-100 theme)
(colors/theme-colors colors/white-opa-10 colors/neutral-100-opa-10 theme)]
:style style/gradient-start}])
(when (and overflow? disabled?)
[linear-gradient/linear-gradient
{:start {:x 0 :y 0}
:end {:x 1 :y 0}
:colors [(colors/theme-colors colors/white-opa-10 colors/neutral-100-opa-10 theme)
(colors/theme-colors colors/white colors/neutral-100 theme)]
:style style/gradient-end}])
[rn/text-input
(cond-> {:ref set-input-ref
:style (style/input disabled? error? theme)
:placeholder-text-color (colors/theme-colors colors/neutral-40
colors/neutral-50
theme)
:keyboard-type :numeric
:editable (not input-disabled?)
:auto-focus auto-focus?
:on-focus on-input-focus
:on-change-text on-change-text
:on-layout on-layout-text-input
:on-selection-change on-selection-change
:show-soft-input-on-focus show-keyboard?
:default-value default-value
:placeholder "0"}
controlled-input? (assoc :value value))]]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)}
{:size :paragraph-2
:weight :semi-bold
:style (style/token-symbol theme)
:on-layout on-layout-label}
token]]
(when (and pay? enable-swap?)
[buttons/button
@@ -17,7 +17,7 @@
(def transaction-icon
{:send :i/send
:swap :i/swap
:swap :i/transaction
:bridge :i/bridge})
(defn transaction-header
@@ -23,7 +23,7 @@
(def transaction-icon
{:receive :i/receive
:send :i/send
:swap :i/swap
:swap :i/transaction
:bridge :i/bridge
:buy :i/buy
:destroy :i/destroy
@@ -96,7 +96,7 @@
(take 7)))
(defn recovery-phrase-screen
[{:keys [banner-offset initial-insets keypair title recovering-keypair? render-controls]}]
[{:keys [banner-offset initial-insets keypair title on-success render-controls]}]
(reagent/with-let [keyboard-shown? (reagent/atom false)
keyboard-show-listener (.addListener rn/keyboard
"keyboardDidShow"
@@ -120,14 +120,20 @@
(reset! seed-phrase new-phrase))
on-submit (fn []
(swap! seed-phrase clean-seed-phrase)
(if recovering-keypair?
(rf/dispatch [:wallet/seed-phrase-entered
(security/mask-data
@seed-phrase)
set-invalid-seed-phrase])
(rf/dispatch [:onboarding/seed-phrase-entered
(security/mask-data @seed-phrase)
set-invalid-seed-phrase])))]
(rf/dispatch
[:profile.recover/validate-recovery-phrase
(security/mask-data @seed-phrase)
{:on-success (fn [mnemonic key-uid]
(if on-success
(on-success
{:key-uid key-uid
:phrase mnemonic
:on-error
set-invalid-seed-phrase})
(rf/dispatch
[:onboarding/seed-phrase-validated
mnemonic key-uid])))
:on-error set-invalid-seed-phrase}]))]
(let [words-coll (mnemonic/passphrase->words @seed-phrase)
last-word (peek words-coll)
pick-suggested-word (fn [pressed-word]
@@ -191,7 +197,7 @@
[rn/view {:style style/keyboard-container}
[quo/predictive-keyboard
{:type suggestions-state
:blur? (not recovering-keypair?)
:blur? (not on-success)
:text suggestions-text
:words (keyboard-suggestions last-word)
:on-press pick-suggested-word}]])])
@@ -200,7 +206,7 @@
(.remove keyboard-hide-listener))))
(defn screen
[{:keys [title keypair navigation-icon recovering-keypair? render-controls]}]
[{:keys [title keypair navigation-icon on-success render-controls]}]
(let [[insets _] (rn/use-state (safe-area/get-insets))
banner-offset (rf/sub [:alert-banners/top-margin])]
[rn/view {:style style/full-layout}
@@ -209,21 +215,22 @@
{:margin-top (:top insets)
:background :blur
:icon-name (or navigation-icon
(if recovering-keypair? :i/close :i/arrow-left))
(if on-success :i/close :i/arrow-left))
:on-press #(rf/dispatch [:navigate-back])}]
[recovery-phrase-screen
{:title title
:keypair keypair
:render-controls render-controls
:banner-offset banner-offset
:initial-insets insets
:recovering-keypair? recovering-keypair?}]]]))
{:title title
:keypair keypair
:render-controls render-controls
:banner-offset banner-offset
:initial-insets insets
:on-success on-success}]]]))
(defn view
[]
(let [{:keys [recovering-keypair?]} (rf/sub [:get-screen-params])]
(let [{:keys [on-success]} (rf/sub [:get-screen-params])]
(rn/use-unmount
#(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen]))
(when-not on-success
#(rf/dispatch [:onboarding/clear-navigated-to-enter-seed-phrase-from-screen])))
[screen
{:title (i18n/label :t/use-recovery-phrase)
:recovering-keypair? recovering-keypair?}]))
{:title (i18n/label :t/use-recovery-phrase)
:on-success on-success}]))
+1
View File
@@ -17,6 +17,7 @@
:generate-keys1 (js/require "../resources/images/ui2/generating-keys-1.png")
:ethereum-address (js/require "../resources/images/ui2/ethereum-address.png")
:use-keycard (js/require "../resources/images/ui2/keycard.png")
:check-your-keycard (js/require "../resources/images/ui2/check-your-keycard.png")
:onboarding-illustration (js/require "../resources/images/ui2/onboarding_illustration.png")
:qr-code (js/require "../resources/images/ui2/qr-code.png")
:keycard-logo (js/require "../resources/images/ui2/keycard-logo.png")
@@ -45,3 +45,15 @@
#(rf/dispatch [:profile.login/login-with-biometric-if-available
(get-in db [:profile/login :key-uid])]))
:shell? true}]]]})))
(rf/reg-fx :effects.centralized-metrics/track
(fn [event]
(native-module/add-centralized-metric event)))
(rf/reg-event-fx
:centralized-metrics/track
(fn [{:keys [db]} [event-name data]]
(let [event-id (name event-name)]
(when (push-event? db)
{:fx [[:effects.centralized-metrics/track
(tracking/key-value-event event-id data)]]}))))
@@ -4,20 +4,20 @@
[react-native.platform :as platform]))
(defn key-value-event
[event-name val-key value]
[event-name event-value]
{:metric
{:eventName event-name
:platform platform/os
:appVersion build/app-short-version
:eventValue {val-key value}}})
:eventValue event-value}})
(defn user-journey-event
[action]
(key-value-event "user-journey" :action action))
(key-value-event "user-journey" {:action action}))
(defn navigation-event
[view-id]
(key-value-event "navigation" :viewId view-id))
(key-value-event "navigation" {:viewId view-id}))
(def ^:const app-started-event "app-started")
@@ -57,7 +57,7 @@
(user-journey-event app-started-event)
:centralized-metrics/toggle-centralized-metrics
(key-value-event "events.metrics-enabled" :enabled second-parameter)
(key-value-event "events.metrics-enabled" {:enabled second-parameter})
:set-view-id
(track-view-id-event second-parameter)
@@ -18,7 +18,7 @@
:platform platform-os
:appVersion app-version
:eventValue {val-key value}}}]
(is (= expected (tracking/key-value-event event-name val-key value))))))
(is (= expected (tracking/key-value-event event-name {val-key value}))))))
(deftest user-journey-event-test
(testing "creates correct user journey event"
@@ -11,25 +11,23 @@
(defn view
[{:keys [contact-id]}]
(let [customization-color (rf/sub [:profile/customization-color])
[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity
contact-id])
{:keys [contact-request-state community-id]} (rf/sub [:chats/current-chat-chat-view])
chat-type (rf/sub [:chats/chat-type])
community-chat? (= chat-type :community-chat)
joined (when community-chat?
(rf/sub [:communities/community-joined
community-id]))
pending? (when community-chat?
(rf/sub [:communities/my-pending-request-to-join
community-id]))
contact-request-send? (or (not contact-request-state)
(= contact-request-state
constants/contact-request-state-none))
contact-request-received? (= contact-request-state
constants/contact-request-state-received)
contact-request-pending? (= contact-request-state
constants/contact-request-state-sent)]
(let [customization-color (rf/sub [:profile/customization-color])
[primary-name _] (rf/sub [:contacts/contact-two-names-by-identity contact-id])
{:keys [contact-request-state
community-id]} (rf/sub [:chats/current-chat-chat-view])
chat-type (rf/sub [:chats/chat-type])
community-chat? (= chat-type :community-chat)
joined (when community-chat?
(rf/sub [:communities/community-joined community-id]))
pending? (when community-chat?
(rf/sub [:communities/my-pending-request-to-join community-id]))
contact-request-send? (or (not contact-request-state)
(= contact-request-state
constants/contact-request-state-none))
contact-request-received? (= contact-request-state
constants/contact-request-state-received)
contact-request-pending? (= contact-request-state
constants/contact-request-state-sent)]
[rn/view {:style style/container}
[quo/permission-context
{:blur? true
@@ -1,11 +1,11 @@
(ns status-im.contexts.chat.messenger.messages.list.style
(:require
[quo.foundations.colors :as colors]
[quo.foundations.shadows :as shadows]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]))
(def permission-context-height 46)
(def distance-from-last-message 4)
(defn keyboard-avoiding-container
[theme]
@@ -13,70 +13,13 @@
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:z-index 2})
(defn background-container
[background-color background-opacity top-margin]
(reanimated/apply-animations-to-style
{:opacity background-opacity}
{:background-color background-color
:position :absolute
:top 0
:left 0
:right 0
:height (+ top-margin messages.constants/header-container-radius)}))
(def permission-context-sheet {:flex 3}) ; Pushes composer to bottom
(defn header-bottom-container
[bottom top-margin]
(reanimated/apply-animations-to-style
{:bottom bottom}
{:margin-top top-margin}))
(defn header-bottom-part
[theme]
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:padding-horizontal 20
:border-radius 20})
(defn header-bottom-shadow
[theme]
(assoc
(shadows/get 2 theme :inverted)
:left 0
:right 0
:height 40
:position :absolute
:border-radius 20))
(defn header-image
[scale top left theme]
(reanimated/apply-animations-to-style
{:transform [{:scale scale}]
:top top
:left left}
{:position :absolute
:border-width 4
:border-radius 50
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
:border-color (colors/theme-colors colors/white colors/neutral-95 theme)}))
(defn user-name-container
[top left]
(reanimated/apply-animations-to-style
{:top top
:left left}
{:z-index -1}))
(def user-name
{:align-items :center
:flex-direction :row
:margin-top 52})
(defn bio-and-actions
[top]
(reanimated/apply-animations-to-style
{:top top}
{:row-gap 16}))
(defn permission-context-sheet
[margin-bottom?]
{:flex 3 ;; Pushes composer to bottom
:margin-bottom (when margin-bottom? permission-context-height)})
(defn list-paddings
[add-padding-bottom?]
(let [{:keys [top bottom]} (safe-area/get-insets)]
;; WARNING: the flat-list is reversed, so the paddings are applied inverted
{:padding-top (if add-padding-bottom?
(+ distance-from-last-message permission-context-height bottom)
distance-from-last-message)
:padding-bottom (+ top messages.constants/top-bar-height)}))
@@ -10,11 +10,9 @@
[react-native.hooks :as hooks]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.common.home.actions.view :as home.actions]
[status-im.constants :as constants]
[status-im.contexts.chat.messenger.composer.constants :as composer.constants]
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]
[status-im.contexts.chat.messenger.messages.content.view :as message]
[status-im.contexts.chat.messenger.messages.list.state :as state]
[status-im.contexts.chat.messenger.messages.list.style :as style]
@@ -24,7 +22,6 @@
[utils.re-frame :as rf]
[utils.worklets.chat.messenger.messages :as worklets]))
(def ^:const distance-from-last-message 4)
(def ^:const loading-indicator-page-loading-height 100)
(defn list-key-fn [{:keys [message-id value]}] (or message-id value))
@@ -194,10 +191,9 @@
context (rf/sub [:chats/current-chat-message-list-view-context])
able-to-send-message? (:able-to-send-message? context)
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
margin-bottom? (and community-channel? (not able-to-send-message?))
recording? (rf/sub [:chats/recording?])
top-margin (+ (safe-area/get-top) messages.constants/top-bar-height)]
[rn/view {:style (style/permission-context-sheet margin-bottom?)}
add-padding-bottom? (and community-channel? (not able-to-send-message?))
recording? (rf/sub [:chats/recording?])]
[rn/view {:style style/permission-context-sheet}
[rn/view {:style {:flex-shrink 1}} ;; Keeps flat list on top
[reanimated/flat-list
{:key-fn list-key-fn
@@ -237,8 +233,7 @@
:style {:background-color (colors/theme-colors colors/white
colors/neutral-95
theme)}
:content-container-style {:padding-top distance-from-last-message
:padding-bottom top-margin}
:content-container-style (style/list-paddings add-padding-bottom?)
:inverted true
:on-layout #(on-layout
{:event %
@@ -0,0 +1,35 @@
(ns status-im.contexts.keycard.authorise.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.standard-authentication.core :as standard-auth]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/authorise-with-password)
:description :context-tag
:context-tag {:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}}]
[rn/view {:style {:flex 1 :padding-horizontal 20 :justify-content :space-between}}
[quo/text (i18n/label :t/migrate-key-pair-authorise)]
[standard-auth/slide-button
{:size :size-48
:container-style {}
:customization-color customization-color
:track-text (i18n/label :t/slide-to-authorise)
:on-auth-success #(println "TBD" (security/safe-unmask-data %))
:auth-button-label (i18n/label :t/confirm)}]]]))
@@ -0,0 +1,36 @@
(ns status-im.contexts.keycard.check.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/check-keycard)
:description :text
:description-text (i18n/label :t/see-keycard-ready)}]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :check-your-keycard)}]]
[quo/divider-label (i18n/label :t/tips-scan-keycard)]
[rn/view {:style {:padding-horizontal 10}}
[quo/markdown-list
{:container-style {:padding-vertical 10}
:description (i18n/label :t/remove-phone-case)}]
[quo/markdown-list
{:container-style {:padding-bottom 25}
:description (i18n/label :t/keep-card-steady)}]]
[quo/button
{:on-press #(rf/dispatch [:keycard/connect])
:container-style {:margin-horizontal 20}}
(i18n/label :t/ready-to-scan)]])
+8 -1
View File
@@ -10,10 +10,15 @@
(defonce ^:private active-listeners (atom []))
(defn register-card-events
(defn unregister-card-events
[]
(doseq [listener @active-listeners]
(keycard/remove-event-listener listener))
(reset! active-listeners nil))
(defn register-card-events
[]
(unregister-card-events)
(reset! active-listeners
[(keycard/on-card-connected #(rf/dispatch [:keycard/on-card-connected]))
(keycard/on-card-disconnected #(rf/dispatch [:keycard/on-card-disconnected]))
@@ -23,7 +28,9 @@
(keycard/on-nfc-timeout #(rf/dispatch [:keycard.ios/on-nfc-timeout])))
(keycard/on-nfc-enabled #(rf/dispatch [:keycard/on-check-nfc-enabled-success true]))
(keycard/on-nfc-disabled #(rf/dispatch [:keycard/on-check-nfc-enabled-success false]))]))
(rf/reg-fx :effects.keycard/register-card-events register-card-events)
(rf/reg-fx :effects.keycard/unregister-card-events unregister-card-events)
(defn check-nfc-enabled
[]
@@ -0,0 +1,36 @@
(ns status-im.contexts.keycard.empty.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[status-im.contexts.keycard.sheets.migrate.view :as sheets.migrate]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/keycard-empty)
:description :text
:description-text (i18n/label :t/what-to-do)}]
[rn/view {:style {:padding-horizontal 28 :padding-top 20}}
[quo/small-option-card
{:variant :main
:title (i18n/label :t/import-key-pair-keycard)
:subtitle (i18n/label :t/use-keycard-login-sign)
:button-label (i18n/label :t/import-profile-key-pair)
:accessibility-label :get-keycard
:image (resources/get-image :generate-keys)
:on-press #(rf/dispatch [:show-bottom-sheet
{:theme :dark
:content (fn [] [sheets.migrate/view])}])}]]
[quo/information-box
{:type :default
:style {:margin-top 32 :margin-horizontal 28}}
(i18n/label :t/empty-card-info)]])
@@ -0,0 +1,42 @@
(ns status-im.contexts.keycard.error.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[utils.re-frame :as rf]))
(def titles
{:keycard/error.keycard-wrong {:title "Keycard is not empty"
:description "You cant use it to store new keys right now"}
:keycard/error.keycard-unpaired {:title "Keycard is full"
:description "All pairing slots are occupied"}
:keycard/error.keycard-frozen {:title "Keycard is locked"
:description "You cant use it right now"}
:keycard/error.keycard-locked {:title "Keycard is locked"
:description "You cant use it right now"}})
(defn view
[]
(let [{:keys [top bottom]} (safe-area/get-insets)
error (rf/sub [:keycard/application-info-error])
{:keys [title description]} (get titles error)]
[quo/overlay
{:type :shell
:container-style {:padding-top top
:padding-bottom bottom}}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title title
:description :text
:description-text description}]
[rn/view {:height 226}]
[rn/view {:padding-horizontal 20}
[quo/info-message
{:container-style {:padding-top 15}
:icon :i/info
:size :default}
"To unlock or factory reset the Keycard, please use the Status desktop app. If you'd like this features on mobile, feel free to upvote them and discuss in the Status community."]]]))
+52 -11
View File
@@ -1,8 +1,8 @@
(ns status-im.contexts.keycard.events
(:require [re-frame.core :as rf]
status-im.contexts.keycard.login.events
status-im.contexts.keycard.nfc-sheet.events
status-im.contexts.keycard.pin.events
status-im.contexts.keycard.sheet.events
status-im.contexts.keycard.sign.events
[status-im.contexts.keycard.utils :as keycard.utils]
[taoensso.timbre :as log]))
@@ -14,7 +14,9 @@
(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled
(fn [{:keys [db]}]
(log/debug "[keycard] nfc user cancelled")
{:db (assoc-in db [:keycard :pin :status] nil)
{:db (-> db
(assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))
:fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])]
[:dispatch on-nfc-cancelled-event-vector])]}))
@@ -42,12 +44,6 @@
{:db (assoc-in db [:keycard :card-connected?] false)
:fx [[:dispatch-later [{:ms 500 :dispatch [:keycard.ios/start-nfc]}]]]}))
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [on-success on-failure]}]]
(log/debug "[keycard] get-application-info")
{:effects.keycard/get-application-info {:on-success on-success
:on-failure on-failure}}))
(rf/reg-event-fx :keycard/on-retrieve-pairings-success
(fn [{:keys [db]} [pairings]]
{:db (assoc-in db [:keycard :pairings] pairings)
@@ -60,7 +56,7 @@
(rf/reg-event-fx :keycard/on-action-with-pin-error
(fn [{:keys [db]} [error]]
(log/debug "[keycard] get keys error: " error)
(log/debug "[keycard] on-action-with-pin-error: " error)
(let [tag-was-lost? (keycard.utils/tag-lost? (:error error))
pin-retries-count (keycard.utils/pin-retries (:error error))]
(if tag-was-lost?
@@ -69,7 +65,52 @@
{:effects.utils/show-popup {:title "wrong-keycard"}}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(update-in [:keycard :pin] assoc :status :error))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
(assoc-in [:keycard :pin :status] :error))
:fx [[:dispatch [:keycard/disconnect]]
(when (zero? pin-retries-count)
[:effects.utils/show-popup {:title "frozen-keycard"}])]})))))
(rf/reg-event-fx :keycard/on-get-application-info-success
(fn [{:keys [db]} [application-info {:keys [key-uid on-success-fx]}]]
(if-let [error (keycard.utils/validate-application-info key-uid application-info)]
(case error
:keycard/error.not-keycard
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.not-keycard]]]}
:keycard/error.keycard-blank
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.empty]]]}
{:db (assoc-in db [:keycard :application-info-error] error)
:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.error]]]})
{:db (-> db
(assoc-in [:keycard :application-info] application-info)
(assoc-in [:keycard :pin :status] :verifying))
:fx on-success-fx})))
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [on-success on-failure]}]]
{:effects.keycard/get-application-info {:on-success on-success :on-failure on-failure}}))
(rf/reg-event-fx :keycard/cancel-connection
(fn [{:keys [db]}]
{:db (-> db
(assoc-in [:keycard :on-card-connected-event-vector] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))}))
(rf/reg-event-fx :keycard/disconnect
(fn [_ _]
{:fx [[:dispatch [:keycard/cancel-connection]]
[:dispatch [:keycard/hide-connection-sheet]]]}))
(rf/reg-event-fx :keycard/connect
(fn [{:keys [db]} [args]]
(let [connected? (get-in db [:keycard :card-connected?])
event-vector [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard/on-get-application-info-success % args])}]]
{:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector)
:fx [[:dispatch
[:keycard/show-connection-sheet
{:on-cancel-event-vector [:keycard/cancel-connection]}]]
(when connected?
[:dispatch event-vector])]})))
@@ -1,7 +1,5 @@
(ns status-im.contexts.keycard.login.events
(:require [status-im.contexts.keycard.utils :as keycard.utils]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(:require [utils.re-frame :as rf]))
(rf/reg-event-fx :keycard.login/on-get-keys-success
(fn [{:keys [db]} [data]]
@@ -16,7 +14,7 @@
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
:fx [[:dispatch [:keycard/disconnect]]
[:effects.keycard/login-with-keycard
{:password encryption-public-key
:whisper-private-key whisper-private-key
@@ -33,36 +31,8 @@
:password encryption-public-key
:key-uid key-uid
:name (:name profile)))
:fx [[:dispatch [:keycard/hide-connection-sheet]]
:fx [[:dispatch [:keycard/disconnect]]
[:effects.keycard/login-with-keycard
{:password encryption-public-key
:whisper-private-key whisper-private-key
:key-uid key-uid}]]}))))
(rf/reg-event-fx :keycard.login/on-get-application-info-success
(fn [{:keys [db]} [application-info {:keys [key-uid on-read-fx]}]]
(let [error (keycard.utils/validate-application-info key-uid application-info)]
(if error
{:effects.utils/show-popup {:title (str error)}}
{:db (-> db
(assoc-in [:keycard :application-info] application-info)
(assoc-in [:keycard :pin :status] :verifying))
:fx on-read-fx}))))
(rf/reg-event-fx :keycard.login/cancel-reading-card
(fn [{:keys [db]}]
{:db (assoc-in db [:keycard :on-card-connected-event-vector] nil)}))
(rf/reg-event-fx :keycard/read-card
(fn [{:keys [db]} [args]]
(let [connected? (get-in db [:keycard :card-connected?])
event-vector [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard.login/on-get-application-info-success %
args])}]]
(log/debug "[keycard] proceed-to-login")
{:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector)
:fx [[:dispatch
[:keycard/show-connection-sheet
{:on-cancel-event-vector [:keycard.login/cancel-reading-card]}]]
(when connected?
[:dispatch event-vector])]})))
@@ -1,18 +1,16 @@
(ns status-im.contexts.keycard.sheet.events
(ns status-im.contexts.keycard.nfc-sheet.events
(:require [re-frame.core :as rf]
[react-native.platform :as platform]
[taoensso.timbre :as log]))
(rf/reg-event-fx :keycard/show-connection-sheet-component
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]]
{:db (assoc-in db [:keycard :connection-sheet-opts] {:on-close #(rf/dispatch on-cancel-event-vector)})
:fx [[:dismiss-keyboard true]
[:show-nfc-sheet nil]]}))
(rf/reg-event-fx :keycard/show-connection-sheet
(fn [_ [args]]
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]} :as args]]
(if platform/android?
{:dispatch [:keycard/show-connection-sheet-component args]}
{:db (assoc-in db
[:keycard :connection-sheet-opts]
{:on-close #(rf/dispatch on-cancel-event-vector)})
:fx [[:dismiss-keyboard true]
[:show-nfc-sheet nil]]}
{:effects.keycard.ios/start-nfc
{:on-success
(fn []
@@ -1,8 +1,9 @@
(ns status-im.contexts.keycard.sheet.view
(ns status-im.contexts.keycard.nfc-sheet.view
(:require [quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn connect-keycard
@@ -18,13 +19,13 @@
:padding-vertical 30
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}}
[rn/text {:style {:font-size 26 :color "#9F9FA5" :margin-bottom 36}}
"Ready to Scan"]
(i18n/label :t/ready-to-scan)]
[rn/image
{:source (resources/get-image :nfc-prompt)}]
[rn/text {:style {:font-size 16 :color :white :margin-vertical 36}}
(if connected?
"Connected. Dont move your card."
"Hold your phone near a Status Keycard")]
(i18n/label :t/connected-dont-move)
(i18n/label :t/hold-phone-near-keycard))]
[rn/pressable
{:on-press (fn []
(when on-close (on-close))
@@ -33,4 +34,4 @@
[rn/view
{:style {:background-color "#8E8E93" :flex 1 :align-items :center :padding 18 :border-radius 10}}
[rn/text {:style {:color :white :font-size 16}}
"Cancel"]]]]]))
(i18n/label :t/cancel)]]]]]))
@@ -0,0 +1,28 @@
(ns status-im.contexts.keycard.not-keycard.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [{:keys [top bottom]} (safe-area/get-insets)]
[quo/overlay
{:type :shell
:container-style {:padding-top top
:padding-bottom bottom}}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/oops-not-keycard)
:description :text
:description-text (i18n/label :t/make-sure-keycard)}]
[rn/view {:flex 1}]
[rn/view {:padding-horizontal 20}
[quo/button {:on-press #(rf/dispatch [:keycard/connect])}
(i18n/label :t/try-again)]]]))
@@ -0,0 +1,55 @@
(ns status-im.contexts.keycard.pin.create.view
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.constants :as constants]
[utils.i18n :as i18n]))
(defn view
[{:keys [on-complete]}]
(let [[pin set-pin] (rn/use-state "")
[first-pin set-first-pin] (rn/use-state "")
[error? set-error] (rn/use-state false)
[stage set-stage] (rn/use-state :create)
pin-empty? (string/blank? pin)
on-delete (rn/use-callback
(fn []
(set-error false)
(if (= (count pin) 1)
(do
(set-pin "")
(set-stage :create))
(when (and pin (pos? (count pin)))
(set-pin (.slice pin 0 -1)))))
[pin])
on-press (rn/use-callback
(fn [new-symbol]
(let [new-pin (str pin new-symbol)]
(when (<= (count new-pin) constants/pincode-length)
(set-pin new-pin)
(when (= constants/pincode-length (count new-pin))
(if (= :repeat stage)
(if (= first-pin new-pin)
(on-complete new-pin)
(set-error true))
(do
(set-pin "")
(set-first-pin new-pin)
(set-stage :repeat)))))))
[pin stage first-pin])]
[rn/view {:style {:padding-bottom 12 :flex 1}}
[quo/drawer-top
{:title (if (= :create stage)
(i18n/label :t/create-keycard-pin)
(i18n/label :t/repeat-keycard-pin))}]
[rn/view {:style {:flex 1 :justify-content :center :align-items :center :padding-vertical 34}}
[quo/pin-input
{:blur? false
:number-of-pins constants/pincode-length
:number-of-filled-pins (count pin)
:error? error?
:info (when error? (i18n/label :t/pin-not-match))}]]
[quo/numbered-keyboard
{:delete-key? (not pin-empty?)
:on-delete on-delete
:on-press on-press}]]))
@@ -0,0 +1,38 @@
(ns status-im.contexts.keycard.sheets.migrate.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])
recovery-phrase-backed-up? (rf/sub [:profile/recovery-phrase-backed-up?])]
[:<>
[quo/drawer-top
{:type :context-tag
:context-tag-type :default
:title (i18n/label :t/migrate-key-pair-keycard)
:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}]
[rn/view {:style {:padding-horizontal 20}}
[quo/text {}
(i18n/label :t/migrate-key-pair-keycard-default-key {:name profile-name})]
[quo/information-box
{:type :default
:style {:margin-top 20 :margin-bottom 12}}
(i18n/label :t/migrate-key-pair-keycard-info)]]
[quo/bottom-actions
{:actions :two-actions
:button-one-label (i18n/label :t/continue)
:button-one-props {:on-press #(if recovery-phrase-backed-up?
(rf/dispatch [:open-modal :screen/use-recovery-phrase
{:on-success (fn [])}])
(rf/dispatch [:open-modal :screen/backup-recovery-phrase
{:on-success (fn [])}]))}
:button-two-label (i18n/label :t/cancel)
:button-two-props {:type :grey
:on-press #(rf/dispatch [:hide-bottom-sheet])}}]]))
+14 -13
View File
@@ -14,16 +14,17 @@
path (get-in db [:wallet :accounts current-address :path])
key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:dispatch
[:keycard/read-card
{:key-uid key-uid
:on-read-fx [[:effects.keycard/sign
{:pin pin-text
:path path
:hash (utils.address/naked-address tx-hash)
:on-success
#(do
(rf/dispatch [:keycard/hide-connection-sheet])
(rf/dispatch
[:wallet/proceed-with-transactions-signatures
(get-signature-map tx-hash %)]))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]]]})))
[:keycard/connect
{:key-uid key-uid
:on-success-fx [[:effects.keycard/sign
{:pin pin-text
:path path
:hash (utils.address/naked-address tx-hash)
:on-success
#(do
(rf/dispatch [:keycard/disconnect])
(rf/dispatch
[:wallet/proceed-with-transactions-signatures
(get-signature-map tx-hash %)]))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]]}]]]})))
+9 -6
View File
@@ -18,28 +18,31 @@
(defn validate-application-info
[profile-key-uid {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}]
(let [profile-mismatch? (or (nil? profile-key-uid) (not= profile-key-uid key-uid))]
(log/debug "[keycard] login-with-keycard"
(log/debug "[keycard]" "login-with-keycard"
"empty application info" (empty? application-info)
"no key-uid" (empty? key-uid)
"profile-mismatch?" profile-mismatch?
"no pairing" paired?)
(cond
(empty? application-info)
:not-keycard
:keycard/error.not-keycard
(empty? (:key-uid application-info))
:keycard-blank
:keycard/error.keycard-blank
profile-mismatch?
:keycard-wrong
:keycard/error.keycard-wrong
(not paired?)
:keycard-unpaired
:keycard/error.keycard-unpaired
(and (zero? pin-retry-counter)
(or (nil? puk-retry-counter)
(pos? puk-retry-counter)))
nil
:keycard/error.keycard-frozen
(zero? puk-retry-counter)
:keycard/error.keycard-locked
:else
nil)))
@@ -131,15 +131,6 @@
[:open-modal :screen/onboarding.enable-biometrics]
[:open-modal :screen/onboarding.enable-notifications])}))
(rf/defn seed-phrase-entered
{:events [:onboarding/seed-phrase-entered]}
[_ seed-phrase on-error]
{:multiaccount/validate-mnemonic [seed-phrase
(fn [mnemonic key-uid]
(re-frame/dispatch [:onboarding/seed-phrase-validated
mnemonic key-uid]))
on-error]})
(rf/defn seed-phrase-validated
{:events [:onboarding/seed-phrase-validated]}
[{:keys [db]} seed-phrase key-uid]
@@ -1,6 +1,7 @@
(ns status-im.contexts.onboarding.syncing.progress.style
(:require
[quo.foundations.colors :as colors]))
[quo.foundations.colors :as colors]
[react-native.safe-area :as safe-area]))
(def absolute-fill
{:position :absolute
@@ -17,6 +18,7 @@
:bottom 0
:left 0
:right 0
:padding-top (safe-area/get-top)
:padding-bottom 20
:background-color (when-not in-onboarding? colors/neutral-80-opa-80-blur)})
@@ -15,16 +15,14 @@
(defn page-title
[pairing-progress?]
[quo/text-combinations
{:container-style {:margin-top 56 :margin-horizontal 20}
:title (i18n/label (if pairing-progress?
:t/sync-devices-title
:t/sync-devices-error-title))
:description (i18n/label (if pairing-progress?
:t/sync-devices-sub-title
:t/sync-devices-error-sub-title))
:title-accessibility-label :progress-screen-title
:description-accessibility-label :progress-screen-sub-title}])
[quo/page-top
{:title (i18n/label (if pairing-progress?
:t/sync-devices-title
:t/sync-devices-error-title))
:description :text
:description-text (i18n/label (if pairing-progress?
:t/sync-devices-sub-title
:t/sync-devices-error-sub-title))}])
(defn- navigate-to-enter-seed-phrase
[]
@@ -42,32 +40,41 @@
(defn try-again-button
[profile-color logged-in?]
[quo/bottom-actions
{:actions (if logged-in? :one-action :two-actions)
:blur? true
:button-one-label (i18n/label :t/recovery-phrase)
:button-one-props {:type :primary
:accessibility-label :try-seed-phrase-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press navigate-to-enter-seed-phrase}
(if logged-in? :button-one-label :button-two-label)
(i18n/label :t/try-again)
(if logged-in? :button-one-props :button-two-props)
{:type (if logged-in? :primary :grey)
:accessibility-label :try-again-later-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press #(try-again logged-in?)}}])
(let [number-of-actions (if logged-in? :one-action :two-actions)
try-again-label (i18n/label :t/try-again)
try-again-props {:type (if logged-in? :primary :grey)
:accessibility-label :try-again-later-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press #(try-again logged-in?)}
recovery-phrase-label (i18n/label :t/recovery-phrase)
recovery-phrase-props {:type :primary
:accessibility-label :try-seed-phrase-button
:customization-color profile-color
:container-style {:flex 1}
:size 40
:on-press navigate-to-enter-seed-phrase}
buttons (if logged-in?
{:button-one-label try-again-label
:button-one-props try-again-props}
{:button-one-label recovery-phrase-label
:button-one-props recovery-phrase-props
:button-two-label try-again-label
:button-two-props try-again-props})]
[quo/bottom-actions
(assoc buttons
:actions number-of-actions
:blur? true)]))
(defn- illustration
[pairing-progress?]
[rn/image
{:resize-mode :contain
:style (style/page-illustration (:width (rn/get-window)))
:source (resources/get-image (if pairing-progress? :syncing-devices :syncing-wrong))}])
:source (resources/get-image (if pairing-progress?
:syncing-devices
:syncing-wrong))}])
(defn view
[in-onboarding?]
@@ -83,7 +90,7 @@
[quo/page-nav {:type :no-title :background :blur}]
[page-title pairing-progress?]
[illustration pairing-progress?]
(when-not (pairing-progress pairing-status)
(when-not pairing-progress?
[try-again-button profile-color logged-in?])]))
(defn view-onboarding
@@ -25,7 +25,7 @@
:title "floating button page"
:description "press right icon to swap button type"
:text-align :left
:right-side [{:icon-name :i/swap
:right-side [{:icon-name :i/transaction
:on-press #(swap! slide? not)}]
:background :blur
:icon-name :i/close
@@ -1,4 +1,4 @@
(ns status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.style
(ns status-im.contexts.profile.backup-recovery-phrase.style
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]))
@@ -1,4 +1,4 @@
(ns status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.view
(ns status-im.contexts.profile.backup-recovery-phrase.view
(:require
[clojure.string :as string]
[native-module.core :as native-module]
@@ -6,8 +6,7 @@
[quo.theme :as quo.theme]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.style :as
style]
[status-im.contexts.profile.backup-recovery-phrase.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
@@ -45,20 +44,20 @@
(defn view
[]
(let [step-labels [:t/backup-step-1 :t/backup-step-2 :t/backup-step-3
:t/backup-step-4]
checked? (reagent/atom
{:0 false
:1 false
:2 false
:3 false})
revealed? (reagent/atom false)
customization-color (rf/sub [:profile/customization-color])
seed-phrase (reagent/atom [])
random-phrase (reagent/atom [])]
(let [step-labels [:t/backup-step-1 :t/backup-step-2 :t/backup-step-3
:t/backup-step-4]
checked? (reagent/atom
{:0 false
:1 false
:2 false
:3 false})
revealed? (reagent/atom false)
customization-color (rf/sub [:profile/customization-color])
{:keys [on-success]} (rf/sub [:get-screen-params])
seed-phrase (reagent/atom [])
random-phrase (reagent/atom [])]
(fn []
(let [theme (quo.theme/use-theme)]
(rn/use-mount
(fn []
(native-module/get-random-mnemonic #(reset! seed-phrase (string/split % #"\s")))
@@ -107,10 +106,10 @@
:button-one-label (i18n/label :t/i-have-written)
:button-one-props {:disabled? (some false? (vals @checked?))
:customization-color customization-color
:on-press #(rf/dispatch [:wallet/store-new-seed-phrase
{:seed-phrase (security/mask-data
@seed-phrase)
:random-phrase @random-phrase}])}}]
:on-press #(on-success {:masked-seed-phrase
(security/mask-data
@seed-phrase)
:random-phrase @random-phrase})}}]
[quo/text
{:size :paragraph-2
:style (style/description-text theme)}
@@ -242,12 +242,13 @@
{:on-complete
(fn [pin-text]
(rf/dispatch
[:keycard/read-card
{:key-uid key-uid
:on-read-fx [[:effects.keycard/get-keys
{:pin pin-text
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]))}]
[:keycard/connect
{:key-uid key-uid
:on-success-fx [[:effects.keycard/get-keys
{:pin pin-text
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]]}]))}]
[password-input])]
(when-not keycard-pairing
[quo/button
@@ -1,9 +1,31 @@
(ns status-im.contexts.profile.recover.effects
(:require
[native-module.core :as native-module]
[utils.re-frame :as rf]))
[promesa.core :as promesa]
[utils.re-frame :as rf]
[utils.security.core :as security]
[utils.transforms :as transforms]))
(rf/reg-fx :effects.profile/restore-and-login
(fn [request]
;;"node.login" signal will be triggered as a callback
(native-module/restore-account-and-login request)))
(defn validate-mnemonic
[mnemonic]
(-> mnemonic
(security/safe-unmask-data)
(native-module/validate-mnemonic)
(promesa/then (fn [result]
(let [{:keys [keyUID]} (transforms/json->clj result)]
{:key-uid keyUID})))))
(rf/reg-fx :effects.profile/validate-recovery-phrase
(fn [[mnemonic on-success on-error]]
(-> (validate-mnemonic mnemonic)
(promesa/then (fn [{:keys [key-uid]}]
(when (fn? on-success)
(on-success mnemonic key-uid))))
(promesa/catch (fn [error]
(when (and error (fn? on-error))
(on-error error)))))))
@@ -24,3 +24,7 @@
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor (or color constants/profile-default-color)
:fetchBackup true)}))
(rf/reg-event-fx :profile.recover/validate-recovery-phrase
(fn [_ [phrase {:keys [on-success on-error]}]]
{:effects.profile/validate-recovery-phrase [phrase on-success on-error]}))
@@ -1,6 +1,7 @@
(ns status-im.contexts.profile.settings.list-items
(:require [status-im.common.not-implemented :as not-implemented]
[status-im.config :as config]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -54,9 +55,9 @@
:image :icon
:blur? true
:action :arrow})
(when config/show-not-implemented-features?
(when (ff/enabled? ::ff/keycard.migrate-profile)
{:title (i18n/label :t/keycard)
:on-press not-implemented/alert
:on-press #(rf/dispatch [:open-modal :screen/settings.keycard])
:image-props :i/keycard
:image :icon
:blur? true
@@ -49,13 +49,14 @@
(defn view
[]
(let [theme (quo.theme/use-theme)
insets (safe-area/get-insets)
customization-color (rf/sub [:profile/customization-color])
scroll-y (reanimated/use-shared-value 0)
profile (rf/sub [:profile/profile])
full-name (profile.utils/displayed-name profile)
on-scroll (rn/use-callback #(scroll-handler % scroll-y))]
(let [theme (quo.theme/use-theme)
insets (safe-area/get-insets)
customization-color (rf/sub [:profile/customization-color])
scroll-y (reanimated/use-shared-value 0)
profile (rf/sub [:profile/profile])
recovery-phrase-backed-up? (rf/sub [:profile/recovery-phrase-backed-up?])
full-name (profile.utils/displayed-name profile)
on-scroll (rn/use-callback #(scroll-handler % scroll-y))]
[quo/overlay {:type :shell}
[rn/view
{:style (style/navigation-wrapper {:customization-color customization-color
@@ -79,7 +80,7 @@
profile)}}])}]}]]
[rn/flat-list
{:header [settings.header/view {:scroll-y scroll-y}]
:data (settings.items/items (boolean (seq (:mnemonic profile))))
:data (settings.items/items (not recovery-phrase-backed-up?))
:shows-vertical-scroll-indicator false
:render-fn settings-category-view
:get-item-layout get-item-layout
@@ -0,0 +1,45 @@
(ns status-im.contexts.settings.keycard.view
(:require [quo.core :as quo]
[quo.foundations.colors :as colors]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [keycard-profile? (rf/sub [:keycard/keycard-profile?])]
[:<>
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/keycard)}]
(if keycard-profile?
[:<>]
[rn/view {:style {:padding-horizontal 28 :padding-top 20}}
[quo/small-option-card
{:variant :main
:title (i18n/label :t/get-keycard)
:subtitle (i18n/label :t/secure-wallet-card)
:button-label (i18n/label :t/buy-keycard)
:accessibility-label :get-keycard
:image (resources/get-image :generate-keys)
:on-press #()}]
[rn/view {:style {:margin-top 24}}
[quo/text
{:style {:margin-bottom 12
:color colors/white-opa-70}
:size :paragraph-2
:weight :medium}
(i18n/label :t/own-keycard)]]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/setup-keycard)
:subtitle (i18n/label :t/ready-keycard)
:accessibility-label :setup-keycard
:image (resources/get-image :use-keycard)
:on-press #(rf/dispatch [:open-modal :screen/keycard.check])}]])]))
@@ -160,7 +160,7 @@
(defn wallet-validate-seed-phrase
[_ [seed-phrase on-success on-error]]
{:fx [[:multiaccount/validate-mnemonic [seed-phrase on-success on-error]]]})
{:fx [[:effects.profile/validate-recovery-phrase [seed-phrase on-success on-error]]]})
(rf/reg-event-fx :wallet/validate-seed-phrase wallet-validate-seed-phrase)
@@ -101,7 +101,7 @@
seed-phrase-masked (security/mask-data "seed phrase")
on-success #(prn "success")
on-error #(prn "error")
expected {:fx [[:multiaccount/validate-mnemonic
expected {:fx [[:effects.profile/validate-recovery-phrase
[seed-phrase-masked on-success on-error]]]}]
(is (= expected
(sut/wallet-validate-seed-phrase
@@ -6,24 +6,21 @@
[react-native.platform :as platform]
[status-im.constants :as constants]
[status-im.contexts.settings.wallet.saved-addresses.sheets.remove-address.view :as remove-address]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[{:keys [name full-address chain-short-names address] :as opts}]
(let [[_ splitted-address] (network-utils/split-network-full-address address)
open-send-flow (rn/use-callback
[{:keys [name full-address chain-short-names address customization-color] :as opts}]
(let [open-send-flow (rn/use-callback
(fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:pop-to-root :shell-stack])
(js/setTimeout #(rf/dispatch [:wallet/select-send-address
{:address full-address
:recipient
{:label
(utils/get-shortened-address
splitted-address)
{:label name
:customization-color
customization-color
:recipient-type :saved-address}
:stack-id :wallet-select-address
:start-flow? true}])
@@ -61,16 +61,6 @@
(rf/reg-event-fx :wallet/seed-phrase-validated seed-phrase-validated)
(defn seed-phrase-entered
[_ [seed-phrase on-error]]
{:fx [[:multiaccount/validate-mnemonic
[seed-phrase
(fn [mnemonic key-uid]
(rf/dispatch [:wallet/seed-phrase-validated mnemonic key-uid on-error]))
on-error]]]})
(rf/reg-event-fx :wallet/seed-phrase-entered seed-phrase-entered)
(defn store-account-generated-with-mnemonic
[{:keys [db]} [{:keys [new-account-data keypair-name]}]]
(let [new-account (update new-account-data :mnemonic security/mask-data)]
@@ -16,13 +16,21 @@
[[{:icon :i/add
:accessibility-label :generate-new-keypair
:label (i18n/label :t/generate-new-keypair)
:on-press #(rf/dispatch [:navigate-to :screen/wallet.backup-recovery-phrase])}
:on-press #(rf/dispatch [:navigate-to :screen/backup-recovery-phrase
{:on-success (fn [{:keys [masked-seed-phrase random-phrase]}]
(rf/dispatch [:wallet/store-new-seed-phrase
{:seed-phrase masked-seed-phrase
:random-phrase
random-phrase}]))}])}
{:icon :i/seed
:accessibility-label :import-using-phrase
:label (i18n/label :t/import-using-phrase)
:add-divider? true
:on-press #(rf/dispatch [:navigate-to :screen/wallet.enter-seed-phrase
{:recovering-keypair? true}])}
:on-press (fn []
(rf/dispatch [:navigate-to :screen/use-recovery-phrase
{:on-success (fn [{:keys [key-uid phrase on-error]}]
(rf/dispatch [:wallet/seed-phrase-validated
phrase key-uid on-error]))}]))}
(when (ff/enabled? ::ff/wallet.import-private-key)
{:icon :i/key
:accessibility-label :import-private-key
@@ -0,0 +1,72 @@
(ns status-im.contexts.wallet.buy-crypto.events
(:require [re-frame.core :as rf]
[react-native.core :as rn]
[status-im.contexts.wallet.sheets.buy-network-selection.view :as buy-network-selection]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[utils.i18n :as i18n]))
(rf/reg-event-fx :wallet.buy-crypto/select-provider
(fn [{:keys [db]} [{:keys [account provider recurrent?]}]]
(let [swap-network (get-in db [:wallet :ui :swap :network])]
{:db (-> db
(assoc-in [:wallet :ui :buy-crypto :account] account)
(assoc-in [:wallet :ui :buy-crypto :provider] provider)
(assoc-in [:wallet :ui :buy-crypto :recurrent?] recurrent?))
:fx [(if swap-network
[:dispatch [:wallet.buy-crypto/select-network swap-network]]
[:dispatch
[:show-bottom-sheet
{:content
(fn []
[buy-network-selection/view
{:provider provider
:account account}])}]])]})))
(rf/reg-event-fx :wallet.buy-crypto/select-network
(fn [{:keys [db]} [network]]
(let [provider (get-in db [:wallet :ui :buy-crypto :provider])
network-chain-id (:chain-id network)
supported-tokens (:supported-tokens provider)
supported-tokens-on-network (set (map :symbol
(filter (fn [token]
(= (:chain-id token) network-chain-id))
supported-tokens)))]
{:db (assoc-in db [:wallet :ui :buy-crypto :network] network)
:fx [[:dispatch
[:show-bottom-sheet
{:content
(fn []
[select-asset/view
{:title (i18n/label :t/select-asset-to-buy)
:network network
:provider provider
:on-select (fn [token]
(rf/dispatch [:wallet/get-crypto-on-ramp-url {:token token}]))
:hide-token-fn (fn [_ token]
(not (contains? supported-tokens-on-network
(:symbol token))))}])}]]]})))
(rf/reg-event-fx
:wallet/get-crypto-on-ramp-url
(fn [{:keys [db]} [{:keys [token]}]]
(let [account (get-in db [:wallet :ui :buy-crypto :account])
provider (get-in db [:wallet :ui :buy-crypto :provider])
network (get-in db [:wallet :ui :buy-crypto :network])
recurrent? (get-in db [:wallet :ui :buy-crypto :recurrent?])
provider-id (:id provider)
parameters {:symbol (:symbol token)
:destAddress (:address account)
:chainID (:chain-id network)
:isRecurrent recurrent?}]
{:fx [[:json-rpc/call
[{:method "wallet_getCryptoOnRampURL"
:params [provider-id parameters]
:on-success (fn [url]
(rf/dispatch [:navigate-back])
(rf/dispatch [:wallet.buy-crypto/clean-all])
(rn/open-url url))
:on-error [:wallet/log-rpc-error {:event :wallet/get-crypto-on-ramps}]}]]]})))
(rf/reg-event-fx :wallet.buy-crypto/clean-all
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :buy-crypto)}))
@@ -8,19 +8,20 @@
[utils.re-frame :as rf]))
(defn get-bottom-sheet-args
[switcher-type]
[switcher-type params]
(case switcher-type
:account-options {:content account-options/view
:hide-handle? true}
:select-account {:content select-account/view}
:select-account {:content (fn []
[select-account/view params])}
nil))
(defn- on-dapps-press
[switcher-type]
(rf/dispatch [:show-bottom-sheet (get-bottom-sheet-args switcher-type)]))
[switcher-type params]
(rf/dispatch [:show-bottom-sheet (get-bottom-sheet-args switcher-type params)]))
(defn view
[{:keys [type on-press accessibility-label icon-name switcher-type margin-top]
[{:keys [type on-press accessibility-label icon-name switcher-type margin-top params]
:or {icon-name :i/close
accessibility-label :top-bar
switcher-type :account-options
@@ -45,6 +46,6 @@
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
:on-press #(on-dapps-press switcher-type)
:on-press #(on-dapps-press switcher-type params)
:emoji emoji
:type (when watch-only? :watch-only)})]}]))
@@ -32,9 +32,11 @@
:selected)}]))
(defn view
[{:keys [content-container-style search-text on-token-press preselected-token-symbol]
[{:keys [content-container-style search-text on-token-press preselected-token-symbol chain-ids]
:or {content-container-style {:padding-horizontal 8}}}]
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered {:query search-text}])
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query search-text
:chain-ids chain-ids}])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])]
[gesture/flat-list
@@ -47,8 +47,8 @@
(rf/dispatch [:wallet/bridge-select-token params]))})
(defn- action-swap
[{:keys [token token-symbol testnet-mode?]}]
{:icon :i/swap
[{:keys [token asset-to-receive token-symbol testnet-mode?]}]
{:icon :i/transaction
:accessibility-label :swap
:label (i18n/label :t/swap)
:disabled? testnet-mode?
@@ -56,6 +56,7 @@
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet.swap/start
{:asset-to-pay (or token {:symbol token-symbol})
:asset-to-receive asset-to-receive
:open-new-screen? true}]))})
(defn- action-manage-tokens
@@ -75,23 +76,26 @@
(defn token-value-drawer
[token watch-only? entry-point]
(let [token-symbol (:token token)
token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query 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?])
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/greater-than (money/bignumber "0"))))
(not selected-account)
(assoc :token-symbol token-symbol
:stack-id :wallet-stack))]
(let [token-symbol (:token token)
token-data (first (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query 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?])
receive-token-symbol (if (= token-symbol "SNT") "ETH" "SNT")
asset-to-receive (rf/sub [:wallet/token-by-symbol receive-token-symbol])
params (cond-> {:start-flow? true
:owners token-owners
:testnet-mode? testnet-mode?
:asset-to-receive asset-to-receive}
selected-account
(assoc :token token-data
:stack-id :screen/wallet.accounts
:has-balance? (-> (get-in token [:values :fiat-unformatted-value])
(money/greater-than (money/bignumber "0"))))
(not selected-account)
(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?))
@@ -15,13 +15,17 @@
[full-name]
(first (string/split full-name #" ")))
(defn cut-fiat-balance-to-two-decimals
[balance]
(defn cut-fiat-balance
[balance decimals]
(let [valid-balance? (and balance
(or (number? balance) (.-toFixed balance)))]
(as-> balance $
(if valid-balance? $ 0)
(.toFixed $ 2))))
(.toFixed $ decimals))))
(defn cut-fiat-balance-to-two-decimals
[balance]
(cut-fiat-balance balance 2))
(defn prettify-balance
[currency-symbol balance]
@@ -151,6 +155,10 @@
[accounts address]
(some #(when (= (:address %) address) %) accounts))
(defn get-default-account
[accounts]
(some #(when (:default-account? %) %) accounts))
(defn calculate-token-fiat-value
"Returns the token fiat value for provided raw balance"
[{:keys [currency balance token]}]
+2 -21
View File
@@ -4,6 +4,7 @@
[native-module.core :as native-module]
[promesa.core :as promesa]
[status-im.common.json-rpc.events :as json-rpc]
[status-im.contexts.profile.recover.effects :as profile.recover.effects]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.security.core :as security]
@@ -26,26 +27,6 @@
:paths paths}
on-success))))
(defn validate-mnemonic
[mnemonic]
(-> mnemonic
(security/safe-unmask-data)
(native-module/validate-mnemonic)
(promesa/then (fn [result]
(let [{:keys [keyUID]} (transforms/json->clj result)]
{:key-uid keyUID})))))
(rf/reg-fx
:multiaccount/validate-mnemonic
(fn [[mnemonic on-success on-error]]
(-> (validate-mnemonic mnemonic)
(promesa/then (fn [{:keys [key-uid]}]
(when (fn? on-success)
(on-success mnemonic key-uid))))
(promesa/catch (fn [error]
(when (and error (fn? on-error))
(on-error error)))))))
(defn create-account-from-private-key
[private-key]
(-> private-key
@@ -81,7 +62,7 @@
(defn import-missing-keypair-by-seed-phrase
[keypair-key-uid seed-phrase password]
(-> (validate-mnemonic seed-phrase)
(-> (profile.recover.effects/validate-mnemonic seed-phrase)
(promesa/then
(fn [{:keys [key-uid]}]
(if (not= keypair-key-uid key-uid)
+133 -118
View File
@@ -28,95 +28,108 @@
{:fx [[:dispatch [:wallet/stop-get-suggested-routes]]
[:dispatch [:wallet/clean-suggested-routes]]]}))
(rf/reg-event-fx :wallet/suggested-routes-success
(fn [{:keys [db]} [suggested-routes-data]]
(let [chosen-route (:best suggested-routes-data)
{:keys [token collectible token-display-name
receiver-networks
receiver-network-values
sender-network-values tx-type
disabled-from-chain-ids
from-locked-amounts]} (get-in db [:wallet :ui :send])
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
token-networks (:networks token)
token-networks-ids (when token-networks
(map #(:chain-id %) token-networks))
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? false})
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? true})
sender-possible-chain-ids (map :chain-id sender-network-values)
sender-network-values (if routes-available?
(send-utils/network-amounts
{:network-values
(if (= tx-type :tx/bridge)
from-network-amounts-by-chain
(send-utils/add-zero-values-to-network-values
from-network-amounts-by-chain
sender-possible-chain-ids))
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:from-locked-amounts from-locked-amounts
:tx-type tx-type
:receiver? false})
(send-utils/reset-loading-network-amounts-to-zero
sender-network-values))
receiver-network-values (if routes-available?
(send-utils/network-amounts
{:network-values to-network-amounts-by-chain
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true})
(cond->
(defn- add-not-enough-assets-data
[send-data chosen-route]
(-> send-data
(assoc :route chosen-route
:loading-suggested-routes? false
:suggested-routes {:best []}
:enough-assets? false)
(update :sender-network-values send-utils/reset-loading-network-amounts-to-zero)
(update :receiver-network-values send-utils/reset-loading-network-amounts-to-zero)))
(rf/reg-event-fx
:wallet/suggested-routes-success
(fn [{:keys [db]} [suggested-routes-data enough-assets?]]
(if-not enough-assets?
{:db (update-in db [:wallet :ui :send] add-not-enough-assets-data (:best suggested-routes-data))}
(let [chosen-route (:best suggested-routes-data)
{:keys [token collectible token-display-name
receiver-networks
receiver-network-values
sender-network-values tx-type
disabled-from-chain-ids
from-locked-amounts]} (get-in db [:wallet :ui :send])
token-decimals (if collectible 0 (:decimals token))
native-token? (and token (= token-display-name "ETH"))
routes-available? (pos? (count chosen-route))
token-networks (:networks token)
token-networks-ids (when token-networks
(map #(:chain-id %) token-networks))
from-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? false})
to-network-amounts-by-chain (send-utils/network-amounts-by-chain
{:route chosen-route
:token-decimals token-decimals
:native-token? native-token?
:receiver? true})
sender-possible-chain-ids (map :chain-id sender-network-values)
sender-network-values (if routes-available?
(send-utils/network-amounts
{:network-values
(if (= tx-type :tx/bridge)
from-network-amounts-by-chain
(send-utils/add-zero-values-to-network-values
from-network-amounts-by-chain
sender-possible-chain-ids))
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:from-locked-amounts from-locked-amounts
:tx-type tx-type
:receiver? false})
(send-utils/reset-loading-network-amounts-to-zero
receiver-network-values)
sender-network-values))
receiver-network-values (if routes-available?
(send-utils/network-amounts
{:network-values to-network-amounts-by-chain
:disabled-chain-ids disabled-from-chain-ids
:receiver-networks receiver-networks
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true})
(cond->
(send-utils/reset-loading-network-amounts-to-zero
receiver-network-values)
(not= tx-type :tx/bridge)
send-utils/safe-add-type-edit))
network-links (when routes-available?
(send-utils/network-links chosen-route
sender-network-values
receiver-network-values))]
{:db (update-in db
[:wallet :ui :send]
assoc
:suggested-routes suggested-routes-data
:route chosen-route
:from-values-by-chain from-network-amounts-by-chain
:to-values-by-chain to-network-amounts-by-chain
:sender-network-values sender-network-values
:receiver-network-values receiver-network-values
:network-links network-links
:loading-suggested-routes? false)})))
(not= tx-type :tx/bridge)
send-utils/safe-add-type-edit))
network-links (when routes-available?
(send-utils/network-links chosen-route
sender-network-values
receiver-network-values))]
{:db (update-in db
[:wallet :ui :send]
assoc
:suggested-routes suggested-routes-data
:route chosen-route
:from-values-by-chain from-network-amounts-by-chain
:to-values-by-chain to-network-amounts-by-chain
:sender-network-values sender-network-values
:receiver-network-values receiver-network-values
:network-links network-links
:loading-suggested-routes? false
:enough-assets? true)}))))
(rf/reg-event-fx :wallet/suggested-routes-error
(rf/reg-event-fx
:wallet/suggested-routes-error
(fn [{:keys [db]} [error-message]]
(let [cleaned-sender-network-values (-> (get-in db [:wallet :ui :send :sender-network-values])
(send-utils/reset-loading-network-amounts-to-zero))
cleaned-receiver-network-values (-> (get-in db [:wallet :ui :send :receiver-network-values])
(send-utils/reset-loading-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 error-message}]]]})))
{:db (-> db
(update-in [:wallet :ui :send] dissoc :route)
(update-in [:wallet :ui :send :sender-network-values]
send-utils/reset-loading-network-amounts-to-zero)
(update-in [:wallet :ui :send :receiver-network-values]
send-utils/reset-loading-network-amounts-to-zero)
(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 error-message}]]]}))
(rf/reg-event-fx :wallet/clean-send-address
(fn [{:keys [db]}]
@@ -378,8 +391,7 @@
from-locked-amounts bridge-to-chain-id]
:or {token updated-token}} (get-in db [:wallet :ui :send])
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
networks ((if test-networks-enabled? :test :prod)
(get-in db [:wallet :networks]))
networks (get-in db [:wallet :networks (if test-networks-enabled? :test :prod)])
network-chain-ids (map :chain-id networks)
token-decimal (when token (:decimals token))
token-id (utils/format-token-id token collectible)
@@ -509,40 +521,43 @@
[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]]
(let [swap? (get-in db [:wallet :ui :swap])
{:keys [code details] :as error-response} (-> data :ErrorResponse)
skip-processing-suggested-routes? (get-in db
[:wallet :ui :send
:skip-processing-suggested-routes?])
process-response? (and (not swap?)
(not skip-processing-suggested-routes?))]
(if (and (not swap?) error-response)
(let [error-message (if (= code "0") "An error occurred" details)]
(log/error "failed to get suggested routes (async)"
{:event :wallet/handle-suggested-routes
:error error-message})
{:fx [(cond
swap?
[:dispatch [:wallet/swap-proposal-error error-message]]
process-response?
[:dispatch [:wallet/suggested-routes-error error-message]])]})
(let [best-routes-fix (comp ->old-route-paths
remove-invalid-bonder-fees-routes
remove-multichain-routes)
candidates-fix (comp ->old-route-paths
remove-invalid-bonder-fees-routes)
routes (-> data
(data-store/rpc->suggested-routes)
(update :best best-routes-fix)
(update :candidates candidates-fix))]
{:fx [(cond
swap?
[:dispatch [:wallet/swap-proposal-success routes]]
process-response?
[:dispatch [:wallet/suggested-routes-success routes]])]})))))
(let [{send :send swap? :swap} (-> db :wallet :ui)
skip-processing-routes? (:skip-processing-suggested-routes? send)]
(when (or swap? (not skip-processing-routes?))
(let [{error-code :code
:as error} (:ErrorResponse data)
enough-assets? (not (and (:Best data) (= error-code "WR-002")))
failure? (and error enough-assets? (not swap?))
error-message (if (zero? error-code) "An error occurred" (:details error))]
(when failure?
(log/error "failed to get suggested routes (async)"
{:event :wallet/handle-suggested-routes
:error error-message}))
{:fx [[:dispatch
(cond
(and failure? swap?) [:wallet/swap-proposal-error error-message]
failure? [:wallet/suggested-routes-error error-message]
swap? [:wallet/swap-proposal-success (fix-routes data)]
:else [:wallet/suggested-routes-success (fix-routes data)
enough-assets?])]]})))))
(rf/reg-event-fx :wallet/add-authorized-transaction
(fn [{:keys [db]} [transaction]]
@@ -41,6 +41,8 @@
:mixedcase-address "0x7bcDfc75c431"
:public-key "0x04371e2d9d66b82f056bc128064"
:removed false}
:wallet/current-viewing-account-color :purple
:wallet/wallet-send-enough-assets? true
:wallet/wallet-send-token {:symbol :eth
:networks [{:source 879
:short-name "eth"
@@ -121,7 +121,7 @@
[quo/alert-banner
{:action? true
:text (i18n/label :t/not-enough-assets-to-pay-gas-fees)
:button-text (i18n/label :t/buy-eth)
:button-text (i18n/label :t/add-eth)
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])}])
@@ -140,6 +140,26 @@
:else (rf/dispatch [:wallet/stop-and-clean-suggested-routes])))
(defn- get-fee-formatted
[route]
(when-let [native-currency-symbol (-> route first :from :native-currency-symbol)]
(rf/sub [:wallet/wallet-send-fee-fiat-formatted native-currency-symbol])))
(defn- insufficient-asset-amount?
[{:keys [token-symbol owned-eth-token input-state no-routes-found? limit-exceeded?
sender-network-values enough-assets?]}]
(let [eth-selected? (= token-symbol (string/upper-case constants/mainnet-short-name))
zero-owned-eth? (money/equal-to (:total-balance owned-eth-token) 0)
input-at-max-owned-amount? (money/equal-to
(controlled-input/value-bn input-state)
(controlled-input/upper-limit-bn input-state))
exceeded-input? (if eth-selected?
input-at-max-owned-amount?
zero-owned-eth?)]
(and (or no-routes-found? limit-exceeded?)
(seq sender-network-values)
(or exceeded-input? (not enough-assets?)))))
(defn view
;; crypto-decimals, limit-crypto and initial-crypto-currency? args are needed
;; for component tests only
@@ -154,147 +174,129 @@
enabled-from-chain-ids :enabled-from-chain-ids
from-enabled-networks :from-enabled-networks
:or {initial-crypto-currency? true}}]
(let [view-id (rf/sub [:view-id])
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
[crypto-currency? set-crypto-currency] (rn/use-state initial-crypto-currency?)
on-navigate-back on-navigate-back
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
(let [view-id (rf/sub [:view-id])
active-screen? (= view-id current-screen-id)
bottom (safe-area/get-bottom)
[crypto-currency?
set-crypto-currency] (rn/use-state initial-crypto-currency?)
handle-on-confirm (fn [amount]
(rf/dispatch [:wallet/set-token-amount-to-send
{:amount amount
:stack-id current-screen-id}]))
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-symbol :symbol
token-networks :networks
:as
token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
:as token} (rf/sub [:wallet/wallet-send-token])
send-from-locked-amounts (rf/sub [:wallet/wallet-send-from-locked-amounts])
{:keys [total-balance]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
usd-conversion-rate (utils/token-usd-price token)
currency (rf/sub [:profile/currency])
conversion-rate (-> token
:market-values-per-currency
currency
:price)
token-decimals (-> token
utils/token-usd-price
utils/one-cent-value
utils/calc-max-crypto-decimals)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(-> (money/crypto->fiat
token-balance
conversion-rate)
utils/cut-fiat-balance-to-two-decimals))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
amount-in-crypto (if crypto-currency?
input-value
(number/remove-trailing-zeroes
(.toFixed (/ input-value conversion-rate)
crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes
(.toFixed total-amount-receiver
(min token-decimals 6)))
" "
token-symbol)
first-route (first route)
native-currency-symbol (when-not confirm-disabled?
(get-in first-route
[:from :native-currency-symbol]))
fee-formatted (when native-currency-symbol
(rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-currency-symbol]))
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub
[:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub
[:wallet/wallet-send-receiver-network-values])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
token-not-supported-in-receiver-networks? (and (not= tx-type :tx/bridge)
(->> receiver-network-values
(remove #(= (:type %) :add))
(every? #(= (:type %) :not-available))))
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-routes?)
(not token-not-supported-in-receiver-networks?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub
[:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-networks-set (set receiver-preferred-networks)
sending-to-unpreferred-networks? (not (every? (fn [receiver-selected-network]
(contains?
receiver-preferred-networks-set
receiver-selected-network))
receiver-networks))
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
should-try-again? (and (not limit-exceeded?) no-routes-found?)
current-address (rf/sub [:wallet/current-viewing-account-address])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case
constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (and
(or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(if (= token-symbol
(string/upper-case
constants/mainnet-short-name))
(money/equal-to
(controlled-input/value-bn input-state)
(controlled-input/upper-limit-bn input-state))
(money/equal-to (:total-balance
owned-eth-token)
0)))
show-no-routes? (and
(or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(not not-enough-asset?))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? routes))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
:as token-by-symbol} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
usd-conversion-rate (utils/token-usd-price token)
currency (rf/sub [:profile/currency])
conversion-rate (-> token
:market-values-per-currency
currency
:price)
token-decimals (-> token
utils/token-usd-price
utils/one-cent-value
utils/calc-max-crypto-decimals)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
usd-conversion-rate)
(utils/cut-fiat-balance-to-two-decimals
(money/crypto->fiat token-balance conversion-rate)))
input-value (controlled-input/input-value input-state)
valid-input? (not (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(number/remove-trailing-zeroes
(.toFixed (/ input-value conversion-rate)
crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes
(.toFixed total-amount-receiver
(min token-decimals 6)))
" "
token-symbol)
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[select-asset-bottom-sheet
clear-input!])}])
sender-network-values (rf/sub [:wallet/wallet-send-sender-network-values])
receiver-network-values (rf/sub [:wallet/wallet-send-receiver-network-values])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
unsupported-token-in-receiver? (and (not= tx-type :tx/bridge)
(->> receiver-network-values
(remove #(= (:type %) :add))
(every? #(= (:type %) :not-available))))
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)
(some? routes)
(not loading-routes?)
(not unsupported-token-in-receiver?))
receiver-networks (rf/sub [:wallet/wallet-send-receiver-networks])
receiver-preferred-networks (rf/sub [:wallet/wallet-send-receiver-preferred-networks])
receiver-preferred-network? (set receiver-preferred-networks)
sending-to-unpreferred-networks? (some (comp not receiver-preferred-network?)
receiver-networks)
input-error (controlled-input/input-error input-state)
limit-exceeded? (controlled-input/upper-limit-exceeded? input-state)
current-address (rf/sub [:wallet/current-viewing-account-address])
current-color (rf/sub [:wallet/current-viewing-account-color])
enough-assets? (rf/sub [:wallet/wallet-send-enough-assets?])
owned-eth-token (rf/sub [:wallet/token-by-symbol
(string/upper-case constants/mainnet-short-name)
enabled-from-chain-ids])
not-enough-asset? (insufficient-asset-amount?
{:enough-assets? enough-assets?
:token-symbol token-symbol
:owned-eth-token owned-eth-token
:input-state input-state
:no-routes-found? no-routes-found?
:limit-exceeded? limit-exceeded?
:sender-network-values sender-network-values})
should-try-again? (and (not limit-exceeded?)
no-routes-found?
(not not-enough-asset?))
show-no-routes? (and (or no-routes-found? limit-exceeded?)
(not-empty sender-network-values)
(not not-enough-asset?))
confirm-disabled? (or (nil? route)
(empty? route)
(not valid-input?))
fee-formatted (when (or (not confirm-disabled?) not-enough-asset?)
(get-fee-formatted route))
request-fetch-routes (fn [bounce-duration-ms]
(fetch-routes
{:amount amount-in-crypto
:valid-input? valid-input?
:bounce-duration-ms bounce-duration-ms
:token token
:reset-amounts-to-zero? (and limit-exceeded?
(some? routes))}))
swap-between-fiat-and-crypto (fn []
(if crypto-currency?
(set-input-state
#(controlled-input/->fiat % conversion-rate))
(set-input-state
#(controlled-input/->crypto % conversion-rate)))
(set-crypto-currency (not crypto-currency?)))]
(rn/use-effect
(fn []
(when active-screen?
@@ -369,38 +371,44 @@
{:token token-by-symbol
:send-amount-in-crypto amount-in-crypto
:valid-input? valid-input?
:token-not-supported-in-receiver-networks? token-not-supported-in-receiver-networks?
:token-not-supported-in-receiver-networks? unsupported-token-in-receiver?
:current-screen-id current-screen-id
:request-fetch-routes request-fetch-routes}]
(when (and (not loading-routes?)
sender-network-values
token-not-supported-in-receiver-networks?)
unsupported-token-in-receiver?)
[token-not-available token-symbol receiver-networks token-networks])
(when (and (not no-routes-found?) (or loading-routes? route))
(when not-enough-asset?
[not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route))
not-enough-asset?)
[estimated-fees
{:loading-routes? loading-routes?
:fees fee-formatted
:amount amount-text}])
(cond
show-no-routes? [no-routes-found]
not-enough-asset? [not-enough-asset])
(when show-no-routes?
[no-routes-found])
[quo/bottom-actions
{:actions :one-action
:button-one-label (if should-try-again?
(i18n/label :t/try-again)
button-one-label)
:button-one-props (merge (when-not should-try-again? button-one-props)
{:disabled? (or loading-routes?
(and (not should-try-again?) confirm-disabled?))
:on-press (cond
should-try-again?
#(rf/dispatch [:wallet/start-get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount-in-crypto))}
:button-one-props (merge (when-not should-try-again?
button-one-props)
{:disabled? (or not-enough-asset?
loading-routes?
(and (not should-try-again?)
confirm-disabled?))
:on-press (cond
should-try-again?
#(rf/dispatch [:wallet/start-get-suggested-routes
{:amount amount-in-crypto
:updated-token token-by-symbol}])
sending-to-unpreferred-networks?
#(show-unpreferred-networks-alert on-confirm)
:else
#(on-confirm amount-in-crypto))
:customization-color current-color}
(when should-try-again?
{:type :grey}))}]
[quo/numbered-keyboard
@@ -10,8 +10,7 @@
[input-amount/view
{:current-screen-id :screen/wallet.send-input-amount
:button-one-label (i18n/label :t/review-send)
:enabled-from-chain-ids (rf/sub
[:wallet/wallet-send-enabled-from-chain-ids])
:enabled-from-chain-ids (rf/sub [:wallet/wallet-send-enabled-from-chain-ids])
:from-enabled-networks (rf/sub [:wallet/wallet-send-enabled-networks])
:on-navigate-back (fn []
(rf/dispatch-sync [:wallet/stop-and-clean-suggested-routes])
@@ -0,0 +1,28 @@
(ns status-im.contexts.wallet.sheets.buy-network-selection.style)
(defn network-list-container
[mainnet?]
{:margin-horizontal 8
:padding-vertical (when mainnet? 8)})
(def header-container
{:height 62
:padding-horizontal 20})
(def context-tag
{:margin-top 0})
(def divider-label
{:padding-top 0
:padding-bottom 0})
(def subheader-container
{:flex-direction :row
:align-items :center
:margin-top 4})
(def on
{:margin-right 4})
(def to
{:margin-horizontal 4})
@@ -0,0 +1,78 @@
(ns status-im.contexts.wallet.sheets.buy-network-selection.view
(:require [quo.core :as quo]
[quo.foundations.resources :as quo.resources]
[react-native.core :as rn]
[status-im.constants :as constants]
[status-im.contexts.wallet.sheets.buy-network-selection.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- network-item
[{:keys [network on-select-network]}]
(let [{:keys [network-name]} network
mainnet? (= network-name constants/mainnet-network-name)]
[quo/network-list
{:label (name network-name)
:network-image (quo.resources/get-network network-name)
:on-press #(on-select-network network)
:container-style (style/network-list-container mainnet?)}]))
(defn- subheader
[{:keys [provider account]}]
[rn/view {:style style/subheader-container}
[quo/text
{:size :paragraph-2
:weight :medium
:style style/on}
(i18n/label :t/on-uppercase)]
[quo/context-tag
{:type :network
:network-logo (:logo-url provider)
:network-name (:name provider)
:size 24
:container-style style/context-tag}]
[quo/text
{:size :paragraph-2
:weight :medium
:style style/to}
(i18n/label :t/to)]
[quo/context-tag
{:type :account
:account-name (:name account)
:emoji (:emoji account)
:size 24
:container-style style/context-tag}]])
(defn view
[]
(let [provider (rf/sub [:wallet/wallet-buy-crypto-provider])
account (rf/sub [:wallet/wallet-buy-crypto-account])
network-details (rf/sub [:wallet/network-details])
mainnet-network (first network-details)
layer-2-networks (rest network-details)
render-fn (rn/use-callback (fn [network]
[network-item
{:network network
:on-select-network
(fn [network]
(rf/dispatch [:wallet.buy-crypto/select-network
network]))}]))]
[:<>
[rn/view {:style style/header-container}
[quo/text
{:size :heading-2
:weight :semi-bold}
(i18n/label :t/select-network-for-buying)]
[subheader
{:provider provider
:account account}]]
(when mainnet-network
[network-item
{:network mainnet-network
:on-select-network #(rf/dispatch [:wallet.buy-crypto/select-network mainnet-network])}])
[quo/divider-label {:container-style style/divider-label}
(i18n/label :t/layer-2)]
[rn/flat-list
{:data (vec layer-2-networks)
:render-fn render-fn
:scroll-enabled false}]]))
@@ -7,11 +7,20 @@
[utils.re-frame :as rf]))
(defn- crypto-on-ramp-item
[{:keys [name description fees logo-url site-url recurrent-site-url]} _ _ {:keys [tab]}]
(let [open-url (rn/use-callback (fn []
(rf/dispatch [:open-url
(if (= tab :recurrent) recurrent-site-url site-url)]))
[site-url recurrent-site-url tab])]
[{:keys [name description fees logo-url site-url recurrent-site-url urls-need-parameters]
:as provider}
_
_
{:keys [account tab]}]
(let [open-url (rn/use-callback
(fn []
(if urls-need-parameters
(rf/dispatch [:wallet.buy-crypto/select-provider
{:account account
:provider provider
:recurrent? (= tab :recurrent)}])
(rn/open-url (if (= tab :recurrent) recurrent-site-url site-url))))
[site-url recurrent-site-url tab])]
[quo/settings-item
{:title name
:description :text
@@ -39,13 +48,14 @@
(rn/use-mount (fn []
(rf/dispatch [:wallet/get-crypto-on-ramps])))
(let [crypto-on-ramps (rf/sub [:wallet/crypto-on-ramps])
account (rf/sub [:wallet/current-viewing-account-or-default])
[selected-tab set-selected-tab] (rn/use-state initial-tab)
[min-height set-min-height] (rn/use-state 0)
on-layout (rn/use-callback
#(set-min-height
(oops/oget % :nativeEvent :layout :height)))]
[:<>
[quo/drawer-top {:title (or title (i18n/label :t/buy-assets))}]
[quo/drawer-top {:title (or title (i18n/label :t/ways-to-buy-assets))}]
[quo/segmented-control
{:size 32
:container-style style/tabs
@@ -58,5 +68,6 @@
(:one-time crypto-on-ramps))
:on-layout on-layout
:style (style/list-container min-height)
:render-data {:tab selected-tab}
:render-data {:tab selected-tab
:account account}
:render-fn crypto-on-ramp-item}]]))
@@ -2,30 +2,60 @@
(:require [quo.core :as quo]
quo.theme
[react-native.gesture :as gesture]
[status-im.contexts.wallet.common.utils :as wallet.utils]
[status-im.contexts.wallet.sheets.select-account.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- render-account-item
[{:keys [color address] :as account} _ _ {:keys [selected-account-address]}]
[quo/account-item
{:type :default
:account-props (assoc account :customization-color color)
:customization-color color
:state (if (= address selected-account-address) :selected :default)
:on-press (fn []
(rf/dispatch [:wallet/switch-current-viewing-account address])
(rf/dispatch [:hide-bottom-sheet]))}])
(defn list-item
[{:keys [account selected-account-address]}]
(let [{:keys [color address]} account]
[quo/account-item
{:type :default
:account-props (assoc account :customization-color color)
:customization-color color
:state (if (= address selected-account-address) :selected :default)
:on-press (fn []
(rf/dispatch [:wallet/switch-current-viewing-account address])
(rf/dispatch [:hide-bottom-sheet]))}]))
(defn list-item-with-balance
[{:keys [account selected-account-address asset-symbol network]}]
(let [{:keys [color address tokens]} account
token (->> tokens
(filter #(= (:symbol %) asset-symbol))
first)
token-balance (get-in token [:balances-per-chain (:chain-id network) :balance])]
[quo/account-item
{:type (if (= address selected-account-address) :default :tag)
:token-props {:symbol asset-symbol
:value (wallet.utils/cut-fiat-balance (or (js/parseFloat token-balance) 0)
4)}
:account-props (assoc account :customization-color color)
:customization-color color
:state (if (= address selected-account-address) :selected :default)
:on-press (fn []
(rf/dispatch [:wallet/switch-current-viewing-account address])
(rf/dispatch [:hide-bottom-sheet]))}]))
(defn view
[]
[{:keys [show-account-balances? asset-symbol network]}]
(let [selected-account-address (rf/sub [:wallet/current-viewing-account-address])
accounts (rf/sub [:wallet/operable-accounts])]
[:<>
[quo/drawer-top {:title (i18n/label :t/select-account)}]
[gesture/flat-list
{:data accounts
:render-fn render-account-item
:render-fn (fn [account _ _ {:keys [selected-account-address]}]
(if show-account-balances?
[list-item-with-balance
{:account account
:selected-account-address selected-account-address
:asset-symbol asset-symbol
:network network}]
[list-item
{:account account
:selected-account-address selected-account-address}]))
:render-data {:selected-account-address selected-account-address}
:content-container-style style/list-container
:shows-vertical-scroll-indicator false}]]))
@@ -51,14 +51,31 @@
:network-name full-name
:type :network}])
(defn- provider-view
[{:keys [name logo-url]}]
[quo/context-tag
{:size 24
:network-logo logo-url
:network-name name
:type :network}])
(defn- subheader-view
[{:keys [account network]}]
[{:keys [provider account network]}]
[rn/view {:style style/subheader-container}
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/select-asset-on)]
[account-view account]
(if provider
[provider-view provider]
[account-view account])
(when provider
[:<>
[quo/text
{:size :paragraph-2
:weight :medium}
(i18n/label :t/to)]
[account-view account]])
[quo/text
{:size :paragraph-2
:weight :medium}
@@ -66,9 +83,8 @@
[network-view network]])
(defn view
[{:keys [title on-select hide-token-fn disable-token-fn]}]
(let [account (rf/sub [:wallet/current-viewing-account])
{:keys [network]} (rf/sub [:wallet/swap])
[{:keys [title network provider on-select hide-token-fn disable-token-fn]}]
(let [account (rf/sub [:wallet/current-viewing-account-or-default])
[search-text set-search-text] (rn/use-state "")
window-height (:height (rn/get-window))]
[rn/safe-area-view {:style (style/container window-height)}
@@ -81,8 +97,9 @@
{:title title
:title-accessibility-label :title-label}]
[subheader-view
{:account account
:network network}]
{:account account
:network network
:provider provider}]
[assets-view
{:search-text search-text
:on-change-text #(set-search-text %)
+8 -16
View File
@@ -14,7 +14,7 @@
[utils.number :as number]))
(rf/reg-event-fx :wallet.swap/start
(fn [{:keys [db]} [{:keys [network open-new-screen?] :as data}]]
(fn [{:keys [db]} [{:keys [network asset-to-receive open-new-screen?] :as data}]]
(let [{:keys [wallet]} db
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
account (swap-utils/wallet-account wallet)
@@ -25,12 +25,6 @@
:account account
:test-networks-enabled? test-networks-enabled?
:token-symbol (get-in data [:asset-to-pay :symbol])}))
asset-to-receive (or (:asset-to-receive data)
(swap-utils/select-default-asset-to-receive
{:wallet wallet
:account account
:test-networks-enabled? test-networks-enabled?
:asset-to-pay asset-to-pay}))
network' (or network
(swap-utils/select-network asset-to-pay))]
{:db (-> db
@@ -76,11 +70,7 @@
(rf/reg-event-fx :wallet.swap/select-asset-to-receive
(fn [{:keys [db]} [{:keys [token]}]]
{:db (update-in db
[:wallet :ui :swap]
#(-> %
(assoc :asset-to-receive token)
(assoc :loading-swap-proposal? true)))}))
{:db (assoc-in db [:wallet :ui :swap :asset-to-receive] token)}))
(rf/reg-event-fx :wallet.swap/set-default-slippage
(fn [{:keys [db]}]
@@ -141,8 +131,6 @@
:amount amount
:amount-hex amount-in-hex
:loading-swap-proposal? true)
:always
(dissoc :error-response)
clean-approval-transaction?
(dissoc :approval-transaction-id :approved-amount :swap-proposal)))
:json-rpc/call [{:method "wallet_getSuggestedRoutesAsync"
@@ -216,6 +204,12 @@
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :swap)}))
(rf/reg-event-fx
:wallet/on-swap-done
(fn [_]
{:fx [[:dispatch [:wallet/select-account-tab :activity]]
[:dispatch [:wallet/clean-swap]]]}))
(rf/reg-event-fx :wallet/swap-transaction
(fn [{:keys [db]} [sha3-pwd]]
(let [wallet-address (get-in db
@@ -310,8 +304,6 @@
:screen/wallet.swap-set-spending-cap
:screen/wallet.swap-confirmation)])
(when-not approval-required?
(rf/dispatch [:wallet/select-account-tab :activity])
(rf/dispatch [:wallet/clean-swap])
(debounce/debounce-and-dispatch
[:toasts/upsert
{:id :swap-transaction-pending
@@ -20,10 +20,15 @@
(defn- assets-view
[search-text on-change-text]
(let [on-token-press (fn [token]
(rf/dispatch [:wallet.swap/start
{:asset-to-pay token
:open-new-screen? false}]))]
(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 (if (= pay-token-symbol "SNT") eth-token snt-token)]
(rf/dispatch [:wallet.swap/start
{:asset-to-pay token
:asset-to-receive asset-to-receive
:open-new-screen? false}])))]
[:<>
[search-input search-text on-change-text]
[asset-list/view
@@ -10,8 +10,12 @@
:height 36
:background-color :transparent})
(def floating-button-page-content-container
{:margin-bottom 168})
(def scroll-view-container
{:padding-bottom 150})
{:flex 1
:padding-bottom 20})
(def content-container
{:padding-top 12
@@ -56,5 +60,5 @@
(defn footer-container
[theme]
{:margin-bottom -10
:background-color (colors/theme-colors colors/white colors/neutral-100 theme)})
{:margin-bottom -12
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
@@ -254,6 +254,7 @@
[rn/view {:style style/container}
[floating-button-page/view
{:footer-container-padding 0
:content-container-style style/floating-button-page-content-container
:header [quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back
@@ -1,27 +1,29 @@
(ns status-im.contexts.wallet.swap.setup-swap.view
(:require [clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.common.events-helper :as events-helper]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings]
[status-im.contexts.wallet.swap.setup-swap.style :as style]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf]
[utils.string :as utils.string]))
(:require
[clojure.string :as string]
[oops.core :as oops]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.platform :as platform]
[react-native.safe-area :as safe-area]
[status-im.common.controlled-input.utils :as controlled-input]
[status-im.common.events-helper :as events-helper]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.select-asset.view :as select-asset]
[status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings]
[status-im.contexts.wallet.swap.setup-swap.style :as style]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf]
[utils.string :as utils.string]))
(def ^:private default-text-for-unfocused-input "0.00")
@@ -85,7 +87,7 @@
[{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}]
(let [account-color (rf/sub [:wallet/current-viewing-account-color])
network (rf/sub [:wallet/swap-network])
asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
pay-token-symbol (:symbol (rf/sub [:wallet/swap-asset-to-pay]))
currency (rf/sub [:profile/currency])
loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
@@ -96,14 +98,19 @@
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])
asset-to-pay (rf/sub [:wallet/token-by-symbol pay-token-symbol])
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)))
pay-input-num-value (controlled-input/value-numeric input-state)
pay-input-amount (controlled-input/input-value input-state)
pay-token-symbol (:symbol asset-to-pay)
pay-token-decimals (:decimals asset-to-pay)
pay-token-balance-selected-chain (get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :balance]
0)
pay-token-balance-selected-chain (number/convert-to-whole-number
(get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :raw-balance]
0)
pay-token-decimals)
pay-token-fiat-value (str
(utils/calculate-token-fiat-value
{:currency currency
@@ -111,10 +118,15 @@
:token asset-to-pay}))
available-crypto-limit (money/bignumber
pay-token-balance-selected-chain)
display-decimals (min pay-token-decimals
constants/min-token-decimals-to-display)
available-crypto-limit-display (number/remove-trailing-zeroes
(.toFixed available-crypto-limit
(min pay-token-decimals
constants/min-token-decimals-to-display)))
(.toFixed available-crypto-limit display-decimals))
available-crypto-limit-display (if (and (= available-crypto-limit-display "0")
(money/greater-than available-crypto-limit
(money/bignumber 0)))
(number/small-number-threshold display-decimals)
available-crypto-limit-display)
approval-amount-required-num (when approval-amount-required
(str (number/hex->whole approval-amount-required
pay-token-decimals)))
@@ -122,8 +134,7 @@
(money/greater-than
(money/bignumber pay-input-amount)
available-crypto-limit))
(money/equal-to (money/bignumber
available-crypto-limit-display)
(money/equal-to available-crypto-limit
(money/bignumber 0)))
valid-pay-input? (and
(not (string/blank?
@@ -137,12 +148,20 @@
:valid-input? valid-pay-input?
:clean-approval-transaction? true}))
[pay-input-amount])]
(rn/use-unmount #(rf/dispatch [:wallet/on-swap-done]))
(rn/use-effect
(fn []
(request-fetch-swap-proposal))
[pay-input-amount])
(rn/use-effect
(fn []
(when-not overlay-shown?
(some-> @input-ref
(oops/ocall "focus"))))
[overlay-shown?])
[quo/swap-input
{:type :pay
{:get-ref set-input-ref
:type :pay
:error? pay-input-error?
:token pay-token-symbol
:customization-color :blue
@@ -156,7 +175,7 @@
:else :disabled)
:currency-symbol currency-symbol
:on-token-press on-token-press
:on-max-press #(on-max-press (str available-crypto-limit))
: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
@@ -239,17 +258,15 @@
(merge {:action? true
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])
:button-text (i18n/label :t/buy-assets)})
:button-text (i18n/label :t/add-assets)})
(= error-response-code
constants/router-error-code-not-enough-native-balance)
(merge {:action? true
:on-button-press #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[buy-token/view
{:title (i18n/label
:t/buy-ethereum)}])}])
:button-text (i18n/label :t/buy-eth)}))]
[buy-token/view])}])
:button-text (i18n/label :t/add-eth)}))]
(when (or pay-input-error? error-response)
[quo/alert-banner props])))
@@ -274,23 +291,28 @@
(defn- pay-token-bottom-sheet
[]
(let [asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])]
(let [asset-to-receive (rf/sub [:wallet/swap-asset-to-receive])
network (rf/sub [:wallet/swap-network])]
[select-asset/view
{:title (i18n/label :t/select-asset-to-pay)
:network network
:on-select (fn [token]
(rf/dispatch [:wallet.swap/select-asset-to-pay {:token token}]))
:hide-token-fn (fn [type {:keys [available-balance]}]
(and (= type constants/swap-tokens-my)
(= available-balance 0)))
:hide-token-fn (fn [type {:keys [balances-per-chain]}]
(let [balance (get-in balances-per-chain [(:chain-id network) :balance] "0")]
(and (= type constants/swap-tokens-my)
(= balance "0"))))
:disable-token-fn (fn [_ token]
(= (:symbol token)
(:symbol asset-to-receive)))}]))
(defn- receive-token-bottom-sheet
[]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])]
(let [asset-to-pay (rf/sub [:wallet/swap-asset-to-pay])
network (rf/sub [:wallet/swap-network])]
[select-asset/view
{:title (i18n/label :t/select-asset-to-receive)
:network network
:on-select (fn [token]
(rf/dispatch [:wallet.swap/select-asset-to-receive {:token token}]))
:disable-token-fn (fn [_ token]
@@ -310,10 +332,12 @@
network (rf/sub [:wallet/swap-network])
pay-input-amount (controlled-input/input-value pay-input-state)
pay-token-decimals (:decimals asset-to-pay)
pay-token-balance-selected-chain (get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :balance]
0)
pay-token-balance-selected-chain (number/convert-to-whole-number
(get-in asset-to-pay
[:balances-per-chain
(:chain-id network) :raw-balance]
0)
pay-token-decimals)
pay-input-error? (and (not (string/blank? pay-input-amount))
(money/greater-than
(money/bignumber pay-input-amount)
@@ -415,13 +439,17 @@
(when (and swap-amount refetch-interval)
(js/clearTimeout @refetch-interval)
(reset! refetch-interval nil))
(if (and swap-amount (not= swap-amount pay-input-amount))
(set-pay-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
swap-amount)))
(refetch-swap-proposal)))))
(cond (and swap-amount (not= swap-amount pay-input-amount))
(set-pay-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
swap-amount)))
(and pay-input-amount
(not (number/valid-decimal-count? pay-input-amount (:decimals asset-to-pay))))
(set-pay-input-state controlled-input/delete-all)
:else
(refetch-swap-proposal)))))
[asset-to-pay])
(rn/use-effect
refetch-swap-proposal
@@ -431,7 +459,10 @@
{:on-press on-close
:icon-name :i/arrow-left
:margin-top (safe-area/get-top)
:switcher-type :select-account}]
:switcher-type :select-account
:params {:show-account-balances? true
:asset-symbol (:symbol asset-to-pay)
:network network}}]
[rn/view {:style style/inputs-container}
[pay-token-input
{:input-state pay-input-state
@@ -453,7 +484,7 @@
:on-token-press #(rf/dispatch [:show-bottom-sheet {:content receive-token-bottom-sheet}])
:on-input-focus #(set-pay-input-focused? false)}]]
[rn/view {:style style/footer-container}
[alert-banner {:pay-input-error? pay-input-error?}]
(when-not loading-swap-proposal? [alert-banner {:pay-input-error? pay-input-error?}])
(when (or loading-swap-proposal? swap-proposal)
[transaction-details])
[action-button {:on-press on-review-swap-press}]]
@@ -80,7 +80,8 @@
:values (send-utils/network-values-for-ui network-values)
:token-props {:token token-symbol
:label (str amount " " token-symbol)
:address (address-utils/get-shortened-compressed-key token-address)
:address (when token-address
(address-utils/get-shortened-compressed-key token-address))
:size 32}}]]))
(defn- pay-section
@@ -98,7 +99,8 @@
:label (i18n/label :t/pay)
:token-symbol pay-token-symbol
:amount pay-amount
:token-address pay-token-address
:token-address (when-not (address-utils/zero-address? pay-token-address)
pay-token-address)
:network network-name
:theme theme}]))
@@ -117,7 +119,8 @@
:label (i18n/label :t/receive)
:token-symbol receive-token-symbol
:amount receive-amount
:token-address receive-token-address
:token-address (when-not (address-utils/zero-address? receive-token-address)
receive-token-address)
:network network-name
:theme theme}]))
@@ -34,9 +34,8 @@
(defn tokens-by-symbol
[props]
(tokens-by-key (assoc props
:key-fn
(fn [{:keys [address] :as token}]
(if (get-in token [:communityData :id])
address
(:symbol token))))))
(tokens-by-key
(assoc props
:key-fn
(fn [{:keys [chainId address] :as token}]
(str chainId "-" (if (get-in token [:communityData :id]) address (:symbol token)))))))
@@ -75,7 +75,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ETH"
:key "1-ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -86,7 +86,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000001"
:decimals 18
:key "SNT"
:key "1-SNT"
:community-id nil
:symbol "SNT"
:sources ["native"]
@@ -174,7 +174,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ETH"
:key "1-ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -185,7 +185,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000001"
:decimals 18
:key "SNT"
:key "1-SNT"
:community-id nil
:symbol "SNT"
:sources ["native" "second"]
@@ -196,7 +196,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000002"
:decimals 18
:key "ANOTHER1"
:key "1-ANOTHER1"
:community-id nil
:symbol "ANOTHER1"
:sources ["second"]
@@ -253,7 +253,7 @@
:image nil})
:by-symbol '({:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ETH"
:key "1-ETH"
:community-id nil
:symbol "ETH"
:sources ["native"]
@@ -264,7 +264,7 @@
:image nil}
{:address "0x0000000000000000000000000000000000000000"
:decimals 18
:key "ANOTHER1"
:key "1-ANOTHER1"
:community-id nil
:symbol "ANOTHER1"
:sources ["native" "second"]
@@ -1,7 +1,6 @@
(ns status-im.contexts.wallet.wallet-connect.events.effects
(:require
[promesa.core :as promesa]
[re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.config :as config]
[status-im.constants :as constants]
@@ -10,6 +9,7 @@
[status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-fx
@@ -56,8 +56,8 @@
:proposal-request proposal-request
:address address
:session-networks session-networks})
(promesa/then on-success)
(promesa/catch on-fail))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-fail)))))
(rf/reg-fx
:effects.wallet-connect/sign-message
@@ -72,8 +72,8 @@
(signing/eth-sign password address data)
(signing/personal-sign password address data))
(promesa/then on-success)
(promesa/catch on-error)))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error))))))
(rf/reg-fx
:effects.wallet-connect/prepare-transaction
@@ -92,8 +92,8 @@
tx-hash
tx-args
chain-id)
(promesa/then on-success)
(promesa/catch on-error))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(rf/reg-fx
:effects.wallet-connect/send-transaction
@@ -103,8 +103,8 @@
tx-hash
tx-args
chain-id)
(promesa/then on-success)
(promesa/catch on-error))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(rf/reg-fx
:effects.wallet-connect/sign-typed-data
@@ -114,8 +114,8 @@
data
chain-id
version)
(promesa/then on-success)
(promesa/catch on-error))))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(rf/reg-fx
:effects.wallet-connect/respond-session-request
@@ -135,17 +135,21 @@
:proposal-request current-proposal
:session-networks session-networks
:address current-address
:on-success #(rf/dispatch [:wallet-connect/approve-session-success %])
:on-fail #(rf/dispatch [:wallet-connect/approve-session-error %])}])
:on-success [:wallet-connect/approve-session-success]
:on-fail [:wallet-connect/approve-session-error]}])
[:dispatch [:dismiss-modal :screen/wallet.wallet-connect-session-proposal]]]}
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
(rf/reg-event-fx :wallet-connect/approve-session-success
(fn [_ [session]]
(log/info "Wallet Connect session approved")
{:fx [[:dispatch [:wallet-connect/on-new-session session]]
[:dispatch [:wallet-connect/reset-current-session-proposal]]
[:dispatch [:wallet-connect/redirect-to-dapp (data-store/get-dapp-redirect-url session)]]]}))
(fn [{:keys [db]} [session]]
(let [total-connected-dapps (data-store/get-total-connected-dapps db)]
{:fx [[:dispatch [:wallet-connect/on-new-session session]]
[:dispatch [:wallet-connect/reset-current-session-proposal]]
[:dispatch [:wallet-connect/redirect-to-dapp (data-store/get-dapp-redirect-url session)]]
[:dispatch
[:centralized-metrics/track :metric/dapp-session-proposal
{:action :approved
:total_connected_dapps total-connected-dapps}]]]})))
(rf/reg-event-fx :wallet-connect/approve-session-error
(fn [_ [error]]
@@ -158,11 +162,18 @@
:wallet-connect/reject-session-proposal
(fn [{:keys [db]} [proposal]]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)]
{:fx [(when-not response-sent?
[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal (or proposal request)
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}])
[:dispatch [:wallet-connect/reset-current-session-proposal]]]})))
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)
networks (networks/get-proposal-networks (or proposal request))
rejected? (nil? proposal)]
{:fx (concat
(when-not response-sent?
[[:effects.wallet-connect/reject-session-proposal
{:web3-wallet web3-wallet
:proposal (or proposal request)
:on-success #(log/info "Wallet Connect session proposal rejected")
:on-error #(log/error "Wallet Connect unable to reject session proposal")}]
[:dispatch
[:centralized-metrics/track :metric/dapp-session-proposal
{:action (if rejected? :rejected :not_supported)
:networks networks}]]])
[[:dispatch [:wallet-connect/reset-current-session-proposal]]])})))
@@ -51,8 +51,8 @@
:address address
:data raw-data
:rpc-method rpc-method
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-typed-data
@@ -65,8 +65,8 @@
:data raw-data
:chain-id chain-id
:version typed-data-version
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-send-transaction-data
@@ -79,8 +79,8 @@
:chain-id chain-id
:tx-hash tx-hash
:tx-args tx-args
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-transaction-data
@@ -93,8 +93,8 @@
:chain-id chain-id
:tx-hash tx-hash
:tx-params tx-args
:on-error #(rf/dispatch [:wallet-connect/on-sign-error %])
:on-success #(rf/dispatch [:wallet-connect/finish-session-request %])}]]})))
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/on-sign-error
@@ -161,9 +161,15 @@
(rf/reg-event-fx
:wallet-connect/finish-session-request
(fn [_ [result]]
{:fx [[:dispatch [:wallet-connect/send-response {:result result}]]
[:dispatch [:wallet-connect/dismiss-request-modal]]]}))
(fn [{:keys [db]} [result]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)]
{:fx [[:dispatch
[:centralized-metrics/track :metric/dapp-session-response
{:method method
:approved true}]]
[:dispatch [:wallet-connect/send-response {:result result}]]
[:dispatch [:wallet-connect/dismiss-request-modal]]]})))
;; NOTE: Currently we only reject a session if the user dismissed a modal
;; without accepting the session first.
@@ -176,9 +182,17 @@
(rf/reg-event-fx
:wallet-connect/on-request-modal-dismissed
(fn [{:keys [db]}]
{:fx [(when-not (get-in db [:wallet-connect/current-request :response-sent?])
[:dispatch
[:wallet-connect/send-response
{:error (wallet-connect/get-sdk-error
constants/wallet-connect-user-rejected-error-key)}]])
[:dispatch [:wallet-connect/reset-current-request]]]}))
(let [{:keys [response-sent? event]} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)]
{:fx (concat
(when-not response-sent?
[[:dispatch
[:centralized-metrics/track :metric/dapp-session-response
{:method method
:approved false}]]
[:dispatch
[:wallet-connect/send-response
{:request event
:error (wallet-connect/get-sdk-error
constants/wallet-connect-user-rejected-error-key)}]]])
[[:dispatch [:wallet-connect/reset-current-request]]])})))
@@ -14,7 +14,10 @@
[{:method "wallet_disconnectWalletConnectSession"
:params [topic]
:on-success [:wallet-connect/delete-session topic]
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]]})))
:on-error #(log/info "Wallet Connect session persistence failed" %)}]]
[:dispatch
[:centralized-metrics/track
:metric/dapp-session-disconnected]]]})))
(rf/reg-event-fx
:wallet-connect/disconnect-dapp
@@ -26,10 +29,14 @@
{:fx [[:effects.wallet-connect/disconnect
{:web3-wallet web3-wallet
:topic topic
:on-fail on-fail
:on-fail (fn []
(when on-fail
(on-fail)))
:on-success (fn []
(log/info "Successfully disconnected dApp session" topic)
(rf/dispatch [:wallet-connect/delete-session topic])
(rf/dispatch [:centralized-metrics/track
:metric/dapp-session-disconnected])
(when on-success
(on-success)))}]]}
{:fx [[:dispatch [:wallet-connect/no-internet-toast]]]}))))
@@ -31,9 +31,12 @@
:not-enough-assets
:t/not-enough-assets-for-transaction)))
:button-text (i18n/label :t/buy-eth)
:on-button-press #(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])}])
:button-text (i18n/label :t/add-eth)
:on-button-press (rn/use-callback (fn []
(rf/dispatch [:centralized-metrics/track
:metric/dapp-buy-eth])
(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])))}])
[rn/view {:style style/content-container}
(into [rn/view
{:style style/data-items-container}]
@@ -73,3 +73,10 @@
(-> db
get-db-current-request-event
get-request-params))
(defn get-total-connected-dapps
[db]
(-> db
:wallet-connect/sessions
count
inc))
@@ -25,3 +25,17 @@
(testing "returns nil if no redirect URL is found"
(let [session {:peer {:metadata {}}}]
(is (nil? (sut/get-dapp-redirect-url session))))))
(deftest get-total-connected-dapps-test
(testing "returns the total number of connected dApps plus 1"
(let [db {:wallet-connect/sessions [{:url "https://dapp1.com"}
{:url "https://dapp2.com"}]}]
(is (= 3 (sut/get-total-connected-dapps db)))))
(testing "returns 1 when there are no connected dApps"
(let [db {:wallet-connect/sessions []}]
(is (= 1 (sut/get-total-connected-dapps db)))))
(testing "handles nil sessions correctly"
(let [db {:wallet-connect/sessions nil}]
(is (= 1 (sut/get-total-connected-dapps db))))))
+1
View File
@@ -41,6 +41,7 @@
status-im.contexts.syncing.events
status-im.contexts.wallet.add-account.add-address-to-watch.events
status-im.contexts.wallet.add-account.create-account.events
status-im.contexts.wallet.buy-crypto.events
status-im.contexts.wallet.collectible.events
status-im.contexts.wallet.common.wizard.events
status-im.contexts.wallet.effects
+2 -1
View File
@@ -32,7 +32,8 @@
::wallet.long-press-watch-only-asset (enabled-in-env? :FLAG_LONG_PRESS_WATCH_ONLY_ASSET_ENABLED)
::wallet.saved-addresses (enabled-in-env? :WALLET_SAVED_ADDRESSES)
::wallet.wallet-connect (enabled-in-env? :FLAG_WALLET_CONNECT_ENABLED)
::wallet.custom-network-amounts (enabled-in-env? :FLAG_WALLET_CUSTOM_NETWORK_AMOUNTS_ENABLED)})
::wallet.custom-network-amounts (enabled-in-env? :FLAG_WALLET_CUSTOM_NETWORK_AMOUNTS_ENABLED)
::keycard.migrate-profile false})
(defonce ^:private feature-flags-config
(reagent/atom initial-flags))
+6
View File
@@ -69,6 +69,12 @@
transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)}))
(def keycard-modal-screen-options
(assoc transparent-modal-screen-options
:layout nil
:theme :dark
:insets {:top? true :bottom? true}))
(def sheet-options
{:layout {:componentBackgroundColor :transparent
:orientation ["portrait"]
+36 -6
View File
@@ -26,6 +26,11 @@
[status-im.contexts.communities.actions.share-community.view :as share-community]
[status-im.contexts.communities.discover.view :as communities.discover]
[status-im.contexts.communities.overview.view :as communities.overview]
[status-im.contexts.keycard.authorise.view :as keycard.authorise]
[status-im.contexts.keycard.check.view :as keycard.check]
[status-im.contexts.keycard.empty.view :as keycard.empty]
[status-im.contexts.keycard.error.view :as keycard.error]
[status-im.contexts.keycard.not-keycard.view :as keycard.not-keycard]
[status-im.contexts.onboarding.create-or-sync-profile.view :as create-or-sync-profile]
[status-im.contexts.onboarding.create-password.view :as create-password]
[status-im.contexts.onboarding.create-profile.view :as create-profile]
@@ -42,6 +47,7 @@
[status-im.contexts.preview.quo.component-preview.view :as component-preview]
[status-im.contexts.preview.quo.main :as quo.preview]
[status-im.contexts.preview.status-im.main :as status-im-preview]
[status-im.contexts.profile.backup-recovery-phrase.view :as backup-recovery-phrase]
[status-im.contexts.profile.contact.share.view :as share-contact]
[status-im.contexts.profile.contact.view :as contact-profile]
[status-im.contexts.profile.edit.accent-colour.view :as edit-accent-colour]
@@ -58,6 +64,7 @@
[status-im.contexts.profile.settings.screens.password.view :as settings-password]
[status-im.contexts.profile.settings.screens.syncing.view :as settings.syncing]
[status-im.contexts.profile.settings.view :as settings]
[status-im.contexts.settings.keycard.view :as settings.keycard]
[status-im.contexts.settings.language-and-currency.currency.view :as settings.currency-selection]
[status-im.contexts.settings.language-and-currency.view :as settings.language-and-currency]
[status-im.contexts.settings.privacy-and-security.share-usage.view :as settings.share-usage]
@@ -101,8 +108,6 @@
wallet-import-private-key]
[status-im.contexts.wallet.add-account.create-account.key-pair-name.view :as
wallet-key-pair-name]
[status-im.contexts.wallet.add-account.create-account.new-keypair.backup-recovery-phrase.view :as
wallet-backup-recovery-phrase]
[status-im.contexts.wallet.add-account.create-account.new-keypair.confirm-backup.view :as
wallet-confirm-backup]
[status-im.contexts.wallet.add-account.create-account.select-keypair.view :as wallet-select-keypair]
@@ -470,9 +475,9 @@
:options {:insets {:top? true}}
:component wallet-create-account/view}
{:name :screen/wallet.backup-recovery-phrase
{:name :screen/backup-recovery-phrase
:options {:insets {:top? true :bottom? true}}
:component wallet-backup-recovery-phrase/view}
:component backup-recovery-phrase/view}
{:name :screen/wallet.confirm-backup
:options {:insets {:top? true :bottom? true}}
@@ -482,7 +487,7 @@
:options {:insets {:top? true}}
:component wallet-key-pair-name/view}
{:name :screen/wallet.enter-seed-phrase
{:name :screen/use-recovery-phrase
:component enter-seed-phrase/view}
{:name :screen/wallet.share-address
@@ -584,6 +589,10 @@
:options options/transparent-modal-screen-options
:component wallet-options/view}
{:name :screen/settings.keycard
:options options/keycard-modal-screen-options
:component settings.keycard/view}
{:name :screen/settings.rename-keypair
:options options/transparent-screen-options
:component keypair-rename/view}
@@ -671,7 +680,28 @@
:popGesture false
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false})
:component change-password-loading/view}]
:component change-password-loading/view}
;; Keycard
{:name :screen/keycard.check
:options options/keycard-modal-screen-options
:component keycard.check/view}
{:name :screen/keycard.empty
:options options/keycard-modal-screen-options
:component keycard.empty/view}
{:name :screen/keycard.error
:options options/keycard-modal-screen-options
:component keycard.error/view}
{:name :screen/keycard.not-keycard
:options options/keycard-modal-screen-options
:component keycard.not-keycard/view}
{:name :screen/keycard.authorise
:options options/keycard-modal-screen-options
:component keycard.authorise/view}]
[{:name :shell
:options {:theme :dark}}]
+2 -2
View File
@@ -11,7 +11,7 @@
[status-im.common.bottom-sheet-screen.view :as bottom-sheet-screen]
[status-im.common.bottom-sheet.view :as bottom-sheet]
[status-im.common.toasts.view :as toasts]
[status-im.contexts.keycard.sheet.view :as keycard.sheet]
[status-im.contexts.keycard.nfc-sheet.view :as keycard.sheet]
[status-im.navigation.screens :as screens]
[status-im.setup.hot-reload :as reloader]
[utils.re-frame :as rf]))
@@ -47,7 +47,7 @@
(merge
{:flex 1
:margin-top alert-banners-top-margin
:background-color (or background-color (colors/theme-colors colors/white colors/neutral-100 theme))}
:background-color (or background-color (colors/theme-colors colors/white colors/neutral-95 theme))}
(when bottom?
{:padding-bottom (safe-area/get-bottom)})
(when top?
+5
View File
@@ -36,3 +36,8 @@
(fn [keycard]
(:connection-sheet-opts keycard)))
(rf/reg-sub
:keycard/application-info-error
:<- [:keycard]
(fn [keycard]
(:application-info-error keycard)))
+12
View File
@@ -326,6 +326,12 @@
(fn [profile]
(profile.utils/photo profile)))
(re-frame/reg-sub
:profile/name
:<- [:profile/profile]
(fn [profile]
(profile.utils/displayed-name profile)))
(re-frame/reg-sub
:profile/login-profile
:<- [:profile/login]
@@ -339,6 +345,12 @@
(fn [{:keys [processing]}]
processing))
(re-frame/reg-sub
:profile/recovery-phrase-backed-up?
:<- [:profile/profile]
(fn [profile]
(not (boolean (seq (:mnemonic profile))))))
;; LINK PREVIEW
;; ========================================================================================================
+25
View File
@@ -5,3 +5,28 @@
:wallet/crypto-on-ramps
:<- [:wallet]
:-> :crypto-on-ramps)
(rf/reg-sub
:wallet/wallet-buy-crypto
:<- [:wallet/ui]
:-> :buy-crypto)
(rf/reg-sub
:wallet/wallet-buy-crypto-account
:<- [:wallet/wallet-buy-crypto]
:-> :account)
(rf/reg-sub
:wallet/wallet-buy-crypto-provider
:<- [:wallet/wallet-buy-crypto]
:-> :provider)
(rf/reg-sub
:wallet/wallet-buy-crypto-recurrent?
:<- [:wallet/wallet-buy-crypto]
:-> :recurrent?)
(rf/reg-sub
:wallet/wallet-buy-crypto-network
:<- [:wallet/wallet-buy-crypto]
:-> :network)
+39 -13
View File
@@ -126,18 +126,23 @@
:<- [:wallet/wallet-send]
:-> :route)
(rf/reg-sub
:wallet/wallet-send-enough-assets?
:<- [:wallet/wallet-send]
:-> :enough-assets?)
(rf/reg-sub
:wallet/wallet-send-token
:<- [:wallet/wallet-send]
:<- [:wallet/network-details]
:<- [:wallet/wallet-send-disabled-from-chain-ids]
(fn [[wallet-send networks disabled-from-chain-ids]]
(let [token (:token wallet-send)
enabled-from-chain-ids (->> networks
(filter #(not (contains? (set disabled-from-chain-ids)
(:chain-id %))))
(map :chain-id)
set)]
(let [token (:token wallet-send)
disabled-from-chain-ids? (set disabled-from-chain-ids)
enabled-from-chain-ids (->> networks
(map :chain-id)
(remove disabled-from-chain-ids?)
set)]
(some-> token
(assoc :networks (network-utils/network-list token networks)
:available-balance (utils/calculate-total-token-balance token)
@@ -399,6 +404,22 @@
(assoc :balance balance
:formatted-balance formatted-balance)))))
(rf/reg-sub
:wallet/current-viewing-account-or-default
:<- [:wallet/accounts]
:<- [:wallet/current-viewing-account-address]
:<- [:wallet/balances-in-selected-networks]
:<- [:profile/currency-symbol]
(fn [[accounts current-viewing-account-address balances currency-symbol]]
(let [account (or (utils/get-account-by-address accounts current-viewing-account-address)
(utils/get-default-account accounts))
address (:address account)
balance (get balances address)
formatted-balance (utils/prettify-balance currency-symbol balance)]
(assoc account
:balance balance
:formatted-balance formatted-balance))))
(rf/reg-sub
:wallet/current-viewing-account-color
:<- [:wallet/current-viewing-account]
@@ -420,7 +441,7 @@
(rf/reg-sub
:wallet/current-viewing-account-tokens-filtered
:<- [:wallet/current-viewing-account]
:<- [:wallet/current-viewing-account-or-default]
:<- [:wallet/network-details]
:<- [:wallet/wallet-send]
(fn [[account networks send-data] [_ {:keys [query chain-ids hide-token-fn]}]]
@@ -454,14 +475,19 @@
(rf/reg-sub
:wallet/tokens-filtered
:<- [:wallet/tokens]
(fn [{:keys [by-symbol market-values-per-token details-per-token]}
:<- [:profile/currency]
(fn [[{:keys [by-symbol market-values-per-token details-per-token prices-per-token]} currency]
[_ {:keys [query chain-ids hide-token-fn]}]]
(let [tokens (->> by-symbol
(map (fn [token]
(-> token
(assoc :market-values
(get market-values-per-token (:symbol token)))
(assoc :details (get details-per-token (:symbol token))))))
(let [token-symbol (keyword (:symbol token))
price (get-in prices-per-token [token-symbol currency])]
(-> token
(assoc :market-values
(get market-values-per-token token-symbol))
(assoc :details (get details-per-token token-symbol))
(assoc-in [:market-values-per-currency currency :price]
price)))))
(filter (fn [{:keys [chain-id]}]
(some #{chain-id} chain-ids)))
(remove #(when hide-token-fn
@@ -476,7 +502,7 @@
(rf/reg-sub
:wallet/token-by-symbol
:<- [:wallet/current-viewing-account]
:<- [:wallet/current-viewing-account-or-default]
:<- [:wallet/network-details]
(fn [[{:keys [tokens]} networks] [_ token-symbol chain-ids]]
(->> (utils/tokens-with-balance tokens networks chain-ids)
+4
View File
@@ -10,6 +10,7 @@
(def regx-eip-3770-address #"^(?:(?:eth:|arb1:|oeth:)(?=:|))*0x[0-9a-fA-F]{40}$")
(def regx-metamask-address #"^ethereum:(0x[0-9a-fA-F]{40})(?:@(0x1|0xa|0xa4b1))?$")
(def regx-address-contains #"(?i)0x[a-fA-F0-9]{40}")
(def zero-address "0x0000000000000000000000000000000000000000")
(defn normalized-hex
[hex]
@@ -137,3 +138,6 @@
(metamask-address? address)
(metamask-address->eth-address address)))
(defn zero-address?
[address]
(= address zero-address))
+10 -8
View File
@@ -47,11 +47,10 @@
(if (bignumber? n) n (bignumber n)))
(defn ->bignumbers
[n1 n2]
(when-let [bn1 (->bignumber n1)]
(when-let [bn2 (->bignumber n2)]
(when (and (bignumber? bn1) (bignumber? bn2))
[bn1 bn2]))))
[& numbers]
(let [transformed-numbers (map ->bignumber numbers)]
(when (every? bignumber? transformed-numbers)
transformed-numbers)))
(defn greater-than-or-equals
[^js n1 ^js n2]
@@ -239,9 +238,12 @@
(defn crypto->fiat
[crypto fiat-price]
(when-let [^js bn (bignumber crypto)]
(-> (.times bn ^js (bignumber fiat-price))
(with-precision 2))))
(let [^js crypto-bn (bignumber crypto)
^js fiat-price-bn (bignumber fiat-price)]
(when (and crypto-bn fiat-price-bn)
(-> crypto-bn
(.times fiat-price-bn)
(with-precision 2)))))
(defn sufficient-funds?
[^js amount ^js balance]
+32 -13
View File
@@ -2,7 +2,8 @@
(:require [clojure.string :as string]
[native-module.core :as native-module]
[utils.hex :as utils.hex]
[utils.money :as utils.money]))
[utils.money :as utils.money]
[utils.money :as money]))
(defn naive-round
"Quickly and naively round number `n` up to `decimal-places`.
@@ -18,17 +19,6 @@
(/ (Math/round (* n scale))
scale)))
(defn convert-to-whole-number
"Converts a fractional `amount` to its corresponding whole number representation
by dividing it by 10 raised to the power of `decimals`. This is often used in financial
calculations where amounts are stored in their smallest units (e.g., cents) and need
to be converted to their whole number equivalents (e.g., dollars).
Example usage:
(convert-to-whole-number 12345 2) ; => 123.45"
[amount decimals]
(/ amount (Math/pow 10 decimals)))
(defn parse-int
"Parses `n` as an integer. Defaults to zero or `default` instead of NaN."
([n]
@@ -65,15 +55,44 @@
"")
""))))
(defn convert-to-whole-number
"Converts a fractional `amount` to its corresponding whole number representation
by dividing it by 10 raised to the power of `decimals`. This is often used in financial
calculations where amounts are stored in their smallest units (e.g., cents) and need
to be converted to their whole number equivalents (e.g., dollars).
Example usage:
(convert-to-whole-number 12345 2) ; => 123.45"
[amount decimals]
(-> amount
(/ (Math/pow 10 decimals))
(.toFixed decimals)
remove-trailing-zeroes))
(defn hex->whole
[num decimals]
(-> num
utils.hex/normalize-hex
native-module/hex-to-number
(convert-to-whole-number decimals)))
(convert-to-whole-number decimals)
money/bignumber))
(defn to-fixed
[num decimals]
(-> num
(utils.money/to-fixed decimals)
remove-trailing-zeroes))
(defn small-number-threshold
"Receives a decimal count and returns a string like '<0.001' if the decimal count is 3,
'<0.000001' if the decimal count is 6, etc."
[decimal-count]
(if (> decimal-count 0)
(str "<0." (apply str (repeat (dec decimal-count) "0")) "1")
"0"))
(defn valid-decimal-count?
"Returns false if the number has more decimals than the decimal count, otherwise true."
[num decimal-count]
(let [decimal-part (second (string/split (str num) #"\."))]
(or (nil? decimal-part) (<= (count decimal-part) decimal-count))))
+44 -11
View File
@@ -5,23 +5,23 @@
(deftest convert-to-whole-number-test
(testing "correctly converts fractional amounts to whole numbers"
(is (= 123.45 (utils.number/convert-to-whole-number 12345 2)))
(is (= 1.2345 (utils.number/convert-to-whole-number 12345 4)))
(is (= 12345.0 (utils.number/convert-to-whole-number 1234500 2)))
(is (= 0.123 (utils.number/convert-to-whole-number 123 3)))
(is (= 1000.0 (utils.number/convert-to-whole-number 1000000 3))))
(is (= "123.45" (utils.number/convert-to-whole-number 12345 2)))
(is (= "1.2345" (utils.number/convert-to-whole-number 12345 4)))
(is (= "12345" (utils.number/convert-to-whole-number 1234500 2)))
(is (= "0.123" (utils.number/convert-to-whole-number 123 3)))
(is (= "1000" (utils.number/convert-to-whole-number 1000000 3))))
(testing "handles zero decimals"
(is (= 12345 (utils.number/convert-to-whole-number 12345 0))))
(is (= "12345" (utils.number/convert-to-whole-number 12345 0))))
(testing "handles negative amounts"
(is (= -123.45 (utils.number/convert-to-whole-number -12345 2)))
(is (= -1.2345 (utils.number/convert-to-whole-number -12345 4)))
(is (= -0.123 (utils.number/convert-to-whole-number -123 3))))
(is (= "-123.45" (utils.number/convert-to-whole-number -12345 2)))
(is (= "-1.2345" (utils.number/convert-to-whole-number -12345 4)))
(is (= "-0.123" (utils.number/convert-to-whole-number -123 3))))
(testing "handles zero amount"
(is (= 0 (utils.number/convert-to-whole-number 0 2)))
(is (= 0 (utils.number/convert-to-whole-number 0 0)))))
(is (= "0" (utils.number/convert-to-whole-number 0 2)))
(is (= "0" (utils.number/convert-to-whole-number 0 0)))))
(deftest parse-int-test
(testing "defaults to zero"
@@ -50,3 +50,36 @@
(is (= 6 (utils.number/parse-float "6")))
(is (= 6.99 (utils.number/parse-float "6.99" 0)))
(is (= -6.9 (utils.number/parse-float "-6.9" 0)))))
(deftest small-number-threshold-test
(testing "correctly generates threshold strings based on decimal count"
(is (= "<0.1" (utils.number/small-number-threshold 1)))
(is (= "<0.01" (utils.number/small-number-threshold 2)))
(is (= "<0.001" (utils.number/small-number-threshold 3)))
(is (= "<0.000001" (utils.number/small-number-threshold 6)))
(is (= "<0.0000000001" (utils.number/small-number-threshold 10)))
(is (= "<0.000000000000000001" (utils.number/small-number-threshold 18)))
(is (= "<0.000000000000000000001" (utils.number/small-number-threshold 21))))
(testing "handles edge cases for decimal count"
(is (= "0" (utils.number/small-number-threshold 0)))
(is (= "0" (utils.number/small-number-threshold -1)))))
(deftest valid-decimal-count?-test
(testing "valid decimal count check for numbers with varying decimals"
(is (true? (utils.number/valid-decimal-count? 123 2)))
(is (true? (utils.number/valid-decimal-count? 123 0)))
(is (true? (utils.number/valid-decimal-count? 123.45 2)))
(is (true? (utils.number/valid-decimal-count? 123.4 2)))
(is (true? (utils.number/valid-decimal-count? 123.456 3)))
(is (false? (utils.number/valid-decimal-count? 123.456 2)))
(is (false? (utils.number/valid-decimal-count? 123.456789 4)))
(is (true? (utils.number/valid-decimal-count? 123.0 1)))
(is (true? (utils.number/valid-decimal-count? -123.45 2)))
(is (false? (utils.number/valid-decimal-count? -123.4567 3)))
(is (true? (utils.number/valid-decimal-count? 1234567890.12 2)))
(is (false? (utils.number/valid-decimal-count? 1234567890.12345 3)))))
+3 -3
View File
@@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im",
"repo": "status-go",
"version": "develop",
"commit-sha1": "ba37c32c07d1b05a94d1c7f7cb1b38fb82730f7b",
"src-sha256": "1f1bbw70iircicda9w0hch56hxr7p1vvzkrk58v8p5sq23xzdf6y"
"version": "v3.3.0",
"commit-sha1": "43659f0c7e1a64102e7f8e38bd532b72714c5819",
"src-sha256": "01fpviw1g22f4ni3li2wnhyz0l517jfipvnyfysckds6yi94l0hf"
}
+1
View File
@@ -125,6 +125,7 @@ class TestrailReport(BaseTestReport):
test_cases['nightly']['activity_centre_contact_request'] = 50984
test_cases['nightly']['activity_centre_other'] = 51005
test_cases['nightly']['wallet'] = 59443
test_cases['nightly']['fallback'] = 63472
## Upgrade e2e
# test_cases['upgrade']['general'] = 881

Some files were not shown because too many files have changed in this diff Show More