Compare commits

..
Author SHA1 Message Date
Ulises M 56962fde77 WIP 2 2025-01-30 10:16:29 -06:00
Ulises M 248bd853a8 WIP: Testing alias from status-go 2025-01-29 21:17:14 -06:00
Yevheniia Berdnyk fa20d4d9c3 e2e: added wallet tests for send and swap flows on mainnet 2025-01-29 18:32:03 +02:00
Parvesh Monu dd26fcf42c Add Documentation drawer on Check your Keycard screen (#21997) 2025-01-29 19:25:34 +05:30
Lungu Cristian 8ac9cc08e1 Keycard signing for dapp interactions (#21785)
* feat: wip dapps signing with keycard

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: hide the pin sheet before showing the connection sheet

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: signing dapp request with password

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: logic for getting the current dapp

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: issues after endpoints update

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* feat: removed support for eth_sign and signTransaction

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: removed unused event

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* feat: re-enable dapps interactions with keycard

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: adapted to latest standard-auth changes

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* feat: enable connected dapps for keycard

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* ref: removed threading (->)

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: get keycard keypair addresses manually

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>

* fix: wrong condition on utils/keycard-address?

---------

Signed-off-by: Cristian Lungu <lungucristian95@gmail.com>
2025-01-28 17:33:34 +02:00
32 changed files with 439 additions and 392 deletions
+11 -7
View File
@@ -204,7 +204,7 @@
(.startSearchForLocalPairingPeers ^js (network) callback))
(defn local-pairing-preflight-outbound-check
"Checks whether the device has allowed connecting to the local server"
"Checks whether the device has allows connecting to the local server"
[callback]
(log/info "[native-module] Performing local pairing preflight check")
(when platform/ios?
@@ -253,15 +253,19 @@
(defn hash-typed-data
"used for keycard"
[data callback]
(log/debug "[native-module] hash-typed-data")
(.hashTypedData ^js (encryption) data callback))
([data]
(native-utils/promisify-native-module-call hash-typed-data data))
([data callback]
(log/debug "[native-module] hash-typed-data")
(.hashTypedData ^js (encryption) data callback)))
(defn hash-typed-data-v4
"used for keycard"
[data callback]
(log/debug "[native-module] hash-typed-data-v4")
(.hashTypedDataV4 ^js (encryption) data callback))
([data]
(native-utils/promisify-native-module-call hash-typed-data-v4 data))
([data callback]
(log/debug "[native-module] hash-typed-data-v4")
(.hashTypedDataV4 ^js (encryption) data callback)))
(defn send-transaction-with-signature
"used for keycard"
@@ -50,8 +50,7 @@
[quo/text
{:weight :semi-bold
:size :heading-2
:style style/header}
(i18n/label :t/check-before-syncing)]
:style style/header} (i18n/label :t/check-before-syncing)]
[quo/text {:style style/description} (i18n/label :t/check-before-syncing-doc-description)]
(into
@@ -70,5 +69,6 @@
:disabled? (not= checked-count (count checks))
:customization-color (or customization-color :blue)
:accessibility-label :continue-button
:on-press on-continue}
(i18n/label :t/continue)]]]))
@@ -1,9 +0,0 @@
(ns status-im.common.enable-access-to-local-network.style)
(def header {:margin-bottom 8})
(def description {:padding-bottom 8
:padding-horizontal 20})
(def list-container {:padding-horizontal 20
:padding-bottom 12})
@@ -1,66 +0,0 @@
(ns status-im.common.enable-access-to-local-network.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.common.enable-access-to-local-network.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- step [{:keys [number text]} button]
[rn/view {:style {:flex-direction :row
:align-items :center
:column-gap 8
:padding-vertical 4}}
[quo/step {:in-blur-view? true} number]
[rn/view {:style {:flex-direction :row
:align-items :center
:column-gap 4}}
[quo/text {:weight :regular
:size :paragraph-2}
text]
button]])
(defn- hide-bottom-sheet
[]
(rf/dispatch [:hide-bottom-sheet]))
(defn view
"This implementation must be only used on iOS since we need to request the networking
permission, on Android we already have the permission granted."
[]
(let [[permission-granted?
set-permission-granted] (rn/use-state false)
verify-permission (rn/use-callback
#(rf/dispatch
[:syncing/preflight-outbound-check set-permission-granted])
[])
verifying-permission? nil]
(prn "permission-granted?:" permission-granted?)
[:<>
[quo/drawer-top {:container-style {:padding-bottom 8}
:title (i18n/label :t/enable-access-to-local-network)
:type :default
:blur? true}]
[quo/text {:style style/description
:weight :regular
:size :paragraph-1}
(i18n/label :t/enable-access-to-local-network-description)]
[rn/view {:style style/list-container}
[step {:number 1 :text "Open"}
[quo/button {:type :primary
:size 24
:on-press (fn [] (js/alert "a"))}
"Settings / Privacy / Local Network"]]
[step {:number 2 :text "Find"}
[quo/button {:type :grey
:size 24
:blur? true}
"Status"]]
[step {:number 3 :text "Toggle the switch to grant access"}]
[step {:number 4 :text "Tap"}
[quo/button {:type (if verifying-permission? :grey :primary)
:size 24
:on-press verify-permission}
(if verifying-permission?
"Verifying local network access..."
"Verify local network access")]]]]))
+1 -6
View File
@@ -221,10 +221,7 @@
(def ^:const wallet-connect-eth-sign-typed-v4-method "eth_signTypedData_v4")
(def ^:const wallet-connect-supported-methods
#{wallet-connect-personal-sign-method
wallet-connect-eth-sign-method
wallet-connect-eth-send-transaction-method
;; NOTE: disabled, as we have no clear use cases for it and other wallets don't support it
;; wallet-connect-eth-sign-transaction-method
wallet-connect-eth-sign-typed-method
wallet-connect-eth-sign-typed-v4-method})
(def ^:const wallet-connect-supported-events #{"accountsChanged" "chainChanged"})
@@ -237,11 +234,9 @@
(def ^:const wallet-connect-message-signing-methods
#{wallet-connect-personal-sign-method
wallet-connect-eth-sign-typed-method
wallet-connect-eth-sign-method
wallet-connect-eth-sign-typed-v4-method})
(def ^:const wallet-connect-transaction-methods
#{wallet-connect-eth-send-transaction-method
wallet-connect-eth-sign-transaction-method})
#{wallet-connect-eth-send-transaction-method})
(def ^:const transaction-pending-type-wallet-connect-transfer "WalletConnectTransfer")
+25 -2
View File
@@ -3,16 +3,39 @@
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- keycard-documentation
[]
[quo/documentation-drawers
{:title (i18n/label :t/keycard)
:shell? true}
[rn/view
[quo/text {:size :paragraph-2}
(i18n/label :t/keycard-documentation)]
[quo/button
{:size 40
:type :primary
:container-style {:margin-top 24 :margin-bottom 12}
:background :blur
:icon-right :i/external
:on-press #(rf/dispatch [:browser.ui/open-url constants/get-keycard-url])
:accessibility-label :get-keycard}
(i18n/label :t/buy-keycard)]]])
(defn view
[]
(let [{:keys [on-press]} (rf/sub [:get-screen-params])]
[:<>
[quo/page-nav
{:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
{:icon-name :i/arrow-left
:on-press events-helper/navigate-back
:right-side [{:icon-name :i/info
:on-press #(rf/dispatch [:show-bottom-sheet
{:content keycard-documentation
:shell? true}])}]}]
[quo/page-top
{:title (i18n/label :t/check-keycard)
:description :text
+13
View File
@@ -76,3 +76,16 @@
args
:on-success (get-on-success args)
:on-failure (get-on-failure args)))
(defn keycard-address?
[keypairs address]
(let [find-keycard-keypair (fn [kps] (some #(when-not (empty? (:keycards %)) %) kps))
keypair-addresses (fn [kp]
(->> (:accounts kp)
(map :address)
set))]
(-> keypairs
vals
find-keycard-keypair
keypair-addresses
(contains? (string/lower-case address)))))
@@ -4,7 +4,6 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.check-before-syncing.view :as check-before-syncing]
[status-im.common.enable-access-to-local-network.view :as enable-access-to-local-network]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[status-im.config :as config]
@@ -25,10 +24,8 @@
[]
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[enable-access-to-local-network/view]
;[check-before-syncing/view {:on-submit navigate-to-sign-in-by-syncing}]
)
{:content (fn [] [check-before-syncing/view
{:on-submit navigate-to-sign-in-by-syncing}])
:shell? true}]))
(defn- navigate-to-sign-in-by-recovery-phrase
+5 -5
View File
@@ -24,11 +24,11 @@
(defn- reduce-profiles
[profiles]
(reduce
(fn [acc {:keys [key-uid] :as profile}]
(assoc acc key-uid (profile.rpc/rpc->profiles-overview profile)))
{}
profiles))
(reduce (fn [acc {:keys [key-uid] :as profile}]
(def --x profile)
(assoc acc key-uid (profile.rpc/rpc->profiles-overview profile)))
{}
profiles))
(rf/reg-fx
:profile/get-profiles-overview
@@ -40,11 +40,15 @@
;; login phase 1: we want to load and show chats faster, so we split login into 2 phases
(rf/reg-event-fx :profile.login/login-existing-profile
(fn [{:keys [db]} [settings-data account]]
(let [settings (data-store.settings/rpc->settings settings-data)
(let [_ (def --sd settings-data)
settings (data-store.settings/rpc->settings settings-data)
_ (def --s settings)
profile-overview (profile.rpc/rpc->profiles-overview account)
_ (def --xx profile-overview)
log-level (or (:log-level settings) config/log-level)
pairing-completed? (= (get-in db [:syncing :pairing-status]) :completed)
biometric-supported-type (get-in db [:biometrics :supported-type])
_ (def --db db)
new-db (-> db
(assoc :profile/profile
(merge profile-overview
@@ -52,7 +56,13 @@
{:log-level log-level}))
(assoc-in [:activity-center :loading?] true)
(dissoc :centralized-metrics/onboarding-enabled?))
_ (def --ndb new-db)
keycard? (get-in new-db [:profile/profile :keycard-pairing])]
(comment
(:compressed-key (:profile/profile --ndb))
(keys --s)
)
{:db (cond-> new-db
pairing-completed? (dissoc :syncing))
:fx (into [[:json-rpc/call
@@ -53,3 +53,4 @@
(def login-profile-card
{:margin-bottom 20})
+5 -1
View File
@@ -16,5 +16,9 @@
(dissoc :customizationColor)
(assoc :customization-color (keyword customizationColor))
(assoc :ens-name? (utils.ens/is-valid-eth-name? (:name profile)))
(assoc :keycard-pairing (when-not (string/blank? keycard-pairing) keycard-pairing)))
(assoc :keycard-pairing (when-not (string/blank? keycard-pairing) keycard-pairing))
(update :name #(if (empty? %)
(:name profile)
;(:alias profile)
(:name profile))))
profile))
+9 -39
View File
@@ -8,7 +8,6 @@
[status-im.contexts.profile.config :as profile.config]
[status-im.contexts.syncing.utils :as sync-utils]
[taoensso.timbre :as log]
[oops.core :as oops]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]
@@ -68,48 +67,19 @@
(re-frame/reg-event-fx :syncing/clear-syncing-fallback-flow clear-syncing-fallback-flow)
#_(rf/defn preflight-outbound-check-for-local-pairing
(rf/defn preflight-outbound-check-for-local-pairing
{:events [:syncing/preflight-outbound-check]}
[_ set-checks-passed]
(let [callback (fn [raw-response]
(log/info "Local pairing preflight check"
{:response raw-response
:event :syncing/preflight-outbound-check})
(let [^js response-js (transforms/json->js raw-response)
error (transforms/js->clj (.-error response-js))]
(set-checks-passed (empty? error))))]
(let [callback
(fn [raw-response]
(log/info "Local pairing preflight check"
{:response raw-response
:event :syncing/preflight-outbound-check})
(let [^js response-js (transforms/json->js raw-response)
error (transforms/js->clj (.-error response-js))]
(set-checks-passed (empty? error))))]
(native-module/local-pairing-preflight-outbound-check callback)))
(rf/reg-fx
:effects/ios-local-pairing-preflight-outbound-check
native-module/local-pairing-preflight-outbound-check)
(defn- outbound-check [set-permission-granted raw-response]
(log/debug "Local pairing preflight check"
{:response raw-response
:event :syncing/preflight-outbound-check})
(def --x raw-response)
(let [error? (-> raw-response transforms/json->js (oops/oget :error) seq)]
(if error?
(rf/dispatch [:toasts/upsert
{:type :negative
:theme :dark
:text "Failed to verify local network access"}])
(set-permission-granted true))
(rf/dispatch [:syncing/set-preflight-outbound-checked])))
(rf/reg-event-fx
:syncing/preflight-outbound-check
(fn [{db :db} [set-permission-granted]]
{:db (assoc db :syncing/checking-preflight-outbound? true)
:fx [[:effects/ios-local-pairing-preflight-outbound-check
(partial outbound-check set-permission-granted)]]}))
(rf/reg-event-fx
:syncing/set-preflight-outbound-checked
(fn [{db :db}]
{:db (dissoc db :syncing/checking-preflight-outbound?)}))
(rf/defn initiate-local-pairing-with-connection-string
{:events [:syncing/input-connection-string-for-bootstrapping]}
[_ connection-string]
@@ -194,7 +194,7 @@
(defn- scan-qr-code-tab
[qr-view-finder]
(if (and ;@preflight-check-passed?
(if (and @preflight-check-passed?
@camera-permission-granted?
(boolean (not-empty qr-view-finder)))
[viewfinder qr-view-finder]
@@ -276,7 +276,7 @@
app-state-listener (.addEventListener rn/app-state "change" set-torch-off-fn)]
#(.remove app-state-listener)))
(defn view
(defn f-view
[_]
(let [insets (safe-area/get-insets)
qr-code-succeed? (reagent/atom false)
@@ -358,7 +358,7 @@
(set-rescan-timeout))
:set-rescan-timeout set-rescan-timeout}])
[rn/view {:style (style/root-container (:top insets))}
[header
[:f> header
{:active-tab active-tab
:title title
:title-opacity title-opacity
@@ -392,3 +392,7 @@
:container-style (style/camera-flash-button @qr-view-finder)
:on-press #(swap! torch? not)}
flashlight-icon])]]))))
(defn view
[props]
[:f> f-view props])
@@ -30,8 +30,7 @@
type :no-title}}]
(let [{:keys [color emoji watch-only?]} (rf/sub [:wallet/current-viewing-account])
networks (rf/sub [:wallet/selected-network-details])
sending-collectible? (rf/sub [:wallet/sending-collectible?])
keycard? (rf/sub [:wallet/selected-keypair-keycard?])]
sending-collectible? (rf/sub [:wallet/sending-collectible?])]
[quo/page-nav
{:type type
:icon-name icon-name
@@ -46,11 +45,7 @@
(not watch-only?)
show-dapps-button?)
{:icon-name :i/dapps
:on-press #(rf/dispatch
(if keycard?
[:keycard/feature-unavailable-show
{:feature-name :wallet.show-connected-dapps}]
[:navigate-to :screen/wallet.connected-dapps]))})
:on-press #(rf/dispatch [:navigate-to :screen/wallet.connected-dapps])})
(when-not sending-collectible?
{:content-type :account-switcher
:customization-color color
@@ -123,3 +123,10 @@
(-> (sign-transaction-hashes hashes address password)
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet/sign-message
(fn [{:keys [message address password on-success on-error]}]
(-> (wallet-rpc/sign-message message address (security/safe-unmask-data password))
(promesa/then on-success)
(promesa/catch on-error))))
+1 -1
View File
@@ -47,7 +47,7 @@
chain-id
constants/transaction-pending-type-wallet-connect-transfer
(transforms/js-stringify tx-args 0)
signature))
(utils.hex/normalize-hex signature)))
(defn sign-message
[message address password]
@@ -1,16 +1,16 @@
(ns status-im.contexts.wallet.wallet-connect.events.effects
(:require
[native-module.core :as native-module]
[promesa.core :as promesa]
[react-native.wallet-connect :as wallet-connect]
[status-im.config :as config]
[status-im.constants :as constants]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.signing :as signing]
[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]))
[utils.transforms :as transforms]))
(rf/reg-fx
:effects.wallet-connect/init
@@ -69,20 +69,21 @@
(promesa/catch (partial rf/call-continuation on-fail)))))
(rf/reg-fx
:effects.wallet-connect/sign-message
(fn [{:keys [password address data rpc-method on-success on-error]}]
(let [password (security/safe-unmask-data password)]
(-> (condp =
rpc-method
:personal-sign
(signing/personal-sign password address data)
:effects.wallet-connect/hash-message
(fn [{:keys [message on-success on-fail]}]
(-> (wallet-rpc/hash-message-eip-191 message)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-fail)))))
:eth-sign
(signing/eth-sign password address data)
(signing/personal-sign password address data))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error))))))
(rf/reg-fx
:effects.wallet-connect/hash-typed-data
(fn [{:keys [message legacy? on-success on-fail]}]
(-> (if legacy?
(native-module/hash-typed-data message)
(native-module/hash-typed-data-v4 message))
(promesa/then (fn [response] (-> response transforms/json->clj :result)))
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-fail)))))
(rf/reg-fx
:effects.wallet-connect/prepare-transaction
@@ -93,36 +94,12 @@
(promesa/then on-success)
(promesa/catch on-error))))
(rf/reg-fx
:effects.wallet-connect/sign-transaction
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
(-> (transactions/sign-transaction (security/safe-unmask-data password)
address
tx-hash
tx-args
chain-id)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(rf/reg-fx
:effects.wallet-connect/send-transaction
(fn [{:keys [password address chain-id tx-hash tx-args on-success on-error]}]
(-> (transactions/send-transaction (security/safe-unmask-data password)
address
tx-hash
tx-args
chain-id)
(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
(fn [{:keys [password address data version chain-id on-success on-error]}]
(-> (typed-data/sign (security/safe-unmask-data password)
address
data
chain-id
version)
(fn [{:keys [chain-id signature tx-args on-success on-error]}]
(-> (wallet-rpc/send-transaction-with-signature chain-id
tx-args
signature)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
@@ -2,7 +2,6 @@
(:require [clojure.string :as string]
[re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.contexts.wallet.data-store :as wallet-data-store]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
@@ -46,15 +45,8 @@
expired? (-> parsed-uri
:expiryTimestamp
uri/timestamp-expired?)
version-supported? (uri/version-supported? version)
keycard? (wallet-data-store/selected-keypair-keycard? db)]
version-supported? (uri/version-supported? version)]
(cond
keycard?
{:fx [[:dispatch
[:keycard/feature-unavailable-show
{:feature-name :wallet.scan-dapp-connection}]]]}
(or (not valid-wc-uri?)
(not version-supported?)
(= network-status :offline)
@@ -41,12 +41,6 @@
[:wallet-connect/process-eth-send-transaction
{:on-success (fn [] (rf/dispatch [:wallet-connect/show-request-modal]))}]]
constants/wallet-connect-eth-sign-method
[:dispatch [:wallet-connect/process-eth-sign]]
constants/wallet-connect-eth-sign-transaction-method
[:dispatch [:wallet-connect/process-eth-sign-transaction]]
constants/wallet-connect-eth-sign-typed-method
[:dispatch [:wallet-connect/process-sign-typed]]
@@ -56,6 +50,13 @@
constants/wallet-connect-personal-sign-method
[:dispatch [:wallet-connect/process-personal-sign]])]}))))
(rf/reg-event-fx
:wallet-connect/store-prepared-hash
(fn [{:keys [db]} [prepared-hash]]
{:db (assoc-in db
[:wallet-connect/current-request :prepared-hash]
prepared-hash)}))
(rf/reg-event-fx
:wallet-connect/process-personal-sign
(fn [{:keys [db]}]
@@ -65,30 +66,20 @@
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:raw-data raw-data
:display-data (or parsed-data raw-data))
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
(rf/reg-event-fx
:wallet-connect/process-eth-sign
(fn [{:keys [db]}]
(let [[address raw-data] (data-store/get-db-current-request-params db)
parsed-data (native-module/hex-to-utf8 raw-data)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
:address (string/lower-case address)
:raw-data raw-data
:display-data (or parsed-data raw-data))
:fx [[:dispatch [:wallet-connect/show-request-modal]]]})))
:fx [[:effects.wallet-connect/hash-message
{:message raw-data
:on-success #(rf/dispatch [:wallet-connect/store-prepared-hash %])
:on-fail #(rf/dispatch [:wallet-connect/on-processing-error %])}]
[:dispatch [:wallet-connect/show-request-modal]]]})))
(rf/reg-event-fx
:wallet-connect/prepare-transaction-success
(fn [{:keys [db]} [prepared-tx chain-id]]
(let [{:keys [tx-args]} prepared-tx
tx (bean/->clj tx-args)
address (-> tx :from string/lower-case)
display-data (transactions/beautify-transaction tx)]
(let [{:keys [tx-args tx-hash]} prepared-tx
tx (bean/->clj tx-args)
address (-> tx :from string/lower-case)
display-data (transactions/beautify-transaction tx)]
{:db (update-in db
[:wallet-connect/current-request]
assoc
@@ -96,7 +87,8 @@
:raw-data prepared-tx
:transaction tx
:chain-id chain-id
:display-data display-data)})))
:display-data display-data)
:fx [[:dispatch [:wallet-connect/store-prepared-hash tx-hash]]]})))
(rf/reg-event-fx
:wallet-connect/process-eth-send-transaction
@@ -116,25 +108,14 @@
(rf/call-continuation on-success)))
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]}))))
(rf/reg-event-fx
:wallet-connect/process-eth-sign-transaction
(fn [{:keys [db]}]
(let [event (data-store/get-db-current-request-event db)
tx (-> event data-store/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
networks/eip155->chain-id)]
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
:on-success #(rf/dispatch [:wallet-connect/prepare-transaction-success % chain-id])
:on-error #(rf/dispatch [:wallet-connect/on-processing-error %])}]]})))
(rf/reg-event-fx
:wallet-connect/process-sign-typed
(fn [{:keys [db]}]
(try
(let [[address raw-data] (data-store/get-db-current-request-params db)
method (-> db
data-store/get-db-current-request-event
data-store/get-request-method)
session-chain-id (-> (data-store/get-db-current-request-event db)
(get-in [:params :chainId])
networks/eip155->chain-id)
@@ -154,7 +135,13 @@
:address (string/lower-case address)
:display-data (typed-data/flatten-typed-data typed-data)
:raw-data raw-data)
:fx [[:dispatch [:wallet-connect/show-request-modal]]]}))
:fx [[:effects.wallet-connect/hash-typed-data
{:message raw-data
:legacy? (not= constants/wallet-connect-eth-sign-typed-v4-method
method)
:on-success #(rf/dispatch [:wallet-connect/store-prepared-hash %])
:on-fail #(rf/dispatch [:wallet-connect/on-processing-error %])}]
[:dispatch [:wallet-connect/show-request-modal]]]}))
(catch js/Error err
{:fx [[:dispatch
[:wallet-connect/on-processing-error
@@ -2,16 +2,47 @@
(:require [re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.constants :as constants]
[status-im.contexts.keycard.utils :as keycard]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.uri :as uri]
[taoensso.timbre :as log]
[utils.hex :as hex]
[utils.i18n :as i18n]
[utils.transforms :as transforms]))
(rf/reg-event-fx
:wallet-connect/respond-current-session
:wallet-connect/authorized-signing
(fn [{:keys [db]} [password]]
(let [prepared-hash (get-in db [:wallet-connect/current-request :prepared-hash])
address (get-in db [:wallet-connect/current-request :address])
keycard-sign? (-> (get-in db [:wallet :keypairs])
(keycard/keycard-address? address))
on-success #(rf/dispatch [:wallet-connect/respond (hex/prefix-hex %)])
on-fail #(rf/dispatch [:wallet-connect/on-sign-error %])]
(if keycard-sign?
{:fx [[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete (fn [pin]
(rf/dispatch [:keycard/connect-and-sign-hashes
{:keycard-pin pin
:address address
:hashes [prepared-hash]
:on-success (fn [signatures]
(rf/dispatch [:hide-bottom-sheet])
(on-success (:signature (first
signatures))))
:on-failure on-fail}]))}]]]}
{:fx [[:effects.wallet/sign-message
{:message prepared-hash
:address address
:password password
:on-success on-success
:on-error on-fail}]]}))))
(rf/reg-event-fx
:wallet-connect/respond
(fn [{:keys [db]} [signature]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)
screen (data-store/method-to-screen method)
@@ -19,83 +50,30 @@
(if (uri/timestamp-expired? expiry)
{:fx [[:dispatch
[:toasts/upsert
{:id :new-wallet-account-created
:type :negative
{:type :negative
:text (i18n/label :t/wallet-connect-request-expired)}]]
[:dispatch [:dismiss-modal screen]]]}
{:fx [(condp = method
constants/wallet-connect-personal-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :personal-sign]]
{:fx [(condp contains? method
#{constants/wallet-connect-personal-sign-method
constants/wallet-connect-eth-sign-typed-method
constants/wallet-connect-eth-sign-typed-v4-method}
[:dispatch [:wallet-connect/finish-session-request signature]]
constants/wallet-connect-eth-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :eth-sign]]
constants/wallet-connect-eth-send-transaction-method
[:dispatch [:wallet-connect/respond-send-transaction-data password]]
constants/wallet-connect-eth-sign-transaction-method
[:dispatch [:wallet-connect/respond-sign-transaction-data password]]
constants/wallet-connect-eth-sign-typed-method
[:dispatch [:wallet-connect/respond-sign-typed-data password :v1]]
constants/wallet-connect-eth-sign-typed-v4-method
[:dispatch [:wallet-connect/respond-sign-typed-data password :v4]])]}))))
(rf/reg-event-fx
:wallet-connect/respond-sign-message
(fn [{:keys [db]} [password rpc-method]]
(let [{:keys [address raw-data]} (get db :wallet-connect/current-request)]
{:fx [[:effects.wallet-connect/sign-message
{:password password
:address address
:data raw-data
:rpc-method rpc-method
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-typed-data
(fn [{:keys [db]} [password typed-data-version]]
(let [{:keys [address raw-data event]} (get db :wallet-connect/current-request)
chain-id (get-in event [:params :chainId])]
{:fx [[:effects.wallet-connect/sign-typed-data
{:password password
:address address
:data raw-data
:chain-id chain-id
:version typed-data-version
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
#{constants/wallet-connect-eth-send-transaction-method}
[:dispatch [:wallet-connect/respond-send-transaction-data signature]])]}))))
(rf/reg-event-fx
:wallet-connect/respond-send-transaction-data
(fn [{:keys [db]} [password]]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data]
(fn [{:keys [db]} [signature]]
(let [{:keys [chain-id raw-data]} (get db :wallet-connect/current-request)
{:keys [tx-args]} raw-data]
{:fx [[:effects.wallet-connect/send-transaction
{:password password
:address address
{:signature signature
:chain-id chain-id
:tx-hash tx-hash
:tx-args tx-args
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/respond-sign-transaction-data
(fn [{:keys [db]} [password]]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data]
{:fx [[:effects.wallet-connect/sign-transaction
{:password password
:address address
:chain-id chain-id
:tx-hash tx-hash
:tx-params tx-args
:on-error [:wallet-connect/on-sign-error]
:on-success [:wallet-connect/finish-session-request]}]]})))
(rf/reg-event-fx
:wallet-connect/on-sign-error
(fn [{:keys [db]} [error]]
@@ -12,13 +12,14 @@
(defn- on-auth-success
[password]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet-connect/respond-current-session password]))
(rf/dispatch [:wallet-connect/authorized-signing password]))
(defn view
[{:keys [warning-label slide-button-text error-state]} & children]
(let [{:keys [customization-color]} (rf/sub [:wallet-connect/current-request-account-details])
offline? (rf/sub [:network/offline?])
theme (quo.theme/use-theme)]
theme (quo.theme/use-theme)
sign-on-keycard? (rf/sub [:wallet-connect/sign-on-keycard?])]
[:<>
(when (or offline? error-state)
[quo/alert-banner
@@ -48,6 +49,8 @@
:disabled? (or offline? error-state)
:customization-color customization-color
:on-auth-success on-auth-success
:on-complete (when sign-on-keycard?
#(on-auth-success ""))
:auth-button-label (i18n/label :t/confirm)}]]
[rn/view {:style style/warning-container}
[quo/text
@@ -30,10 +30,8 @@
(def method-to-screen
{constants/wallet-connect-personal-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-v4-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-send-transaction-method :screen/wallet-connect.send-transaction
constants/wallet-connect-eth-sign-transaction-method :screen/wallet-connect.sign-transaction})
constants/wallet-connect-eth-send-transaction-method :screen/wallet-connect.send-transaction})
(defn extract-native-call-signature
[data]
@@ -59,11 +57,11 @@
(defn get-current-request-dapp
[request sessions]
(let [dapp-url (get-in request [:event :verifyContext :verified :origin])]
(let [request-topic (get-in request [:event :topic])]
(->> sessions
(filter (fn [session]
(= (utils.string/remove-trailing-slash dapp-url)
(utils.string/remove-trailing-slash (get session :url)))))
(= (:topic session)
request-topic)))
first)))
(defn get-dapp-redirect-url
@@ -8,15 +8,15 @@
(deftest get-current-request-dapp-test
(testing "returns the correct dapp based on the request's origin"
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
sessions [{:url "https://dapp.com"}
{:url "https://anotherdapp.com"}]]
(is (= {:url "https://dapp.com"}
(let [request {:event {:topic "123"}}
sessions [{:topic "123"}
{:topic "456"}]]
(is (= {:topic "123"}
(sut/get-current-request-dapp request sessions)))))
(testing "returns nil if no matching dapp is found"
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
sessions [{:url "https://anotherdapp.com"}]]
(let [request {:event {:topic "123"}}
sessions [{:topic "456"}]]
(is (nil? (sut/get-current-request-dapp request sessions))))))
(deftest get-dapp-redirect-url-test
@@ -1,24 +0,0 @@
(ns status-im.contexts.wallet.wallet-connect.utils.signing
(:require
[native-module.core :as native-module]
[promesa.core :as promesa]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[utils.hex :as hex]
[utils.transforms :as transforms]))
(defn eth-sign
[password address data]
(-> {:data data
:account address
:password password}
transforms/clj->json
native-module/sign-message
(promesa/then data-store/extract-native-call-signature)))
(defn personal-sign
[password address data]
(-> (wallet-rpc/hash-message-eip-191 data)
(promesa/then #(wallet-rpc/sign-message % address password))
(promesa/then hex/prefix-hex)))
@@ -14,8 +14,7 @@
(defn transaction-request?
[event]
(->> (data-store/get-request-method event)
(contains? #{constants/wallet-connect-eth-send-transaction-method
constants/wallet-connect-eth-sign-transaction-method})))
(contains? constants/wallet-connect-transaction-methods)))
;; NOTE: Currently we don't allow the user to configure the tx priority as we don't
;; show the estimated time, but when we implement it, we should allow to change it
@@ -136,22 +135,6 @@
:suggested-fees suggested-fees
:estimated-time estimated-time}))
(defn sign-transaction
[password address tx-hash tx-args chain-id]
(promesa/let
[signature (wallet-rpc/sign-message tx-hash address password)
raw-tx (wallet-rpc/build-raw-transaction chain-id tx-args signature)]
raw-tx))
(defn send-transaction
[password address tx-hash tx-args chain-id]
(promesa/let
[signature (wallet-rpc/sign-message tx-hash address password)
tx (wallet-rpc/send-transaction-with-signature chain-id
tx-args
signature)]
tx))
(defn transactions->display-array
[data]
(remove (fn [[k v]]
@@ -6,7 +6,7 @@
{:server-enabled? (config/enabled? config/STATUS_BACKEND_SERVER_ENABLED)
:status-go-endpoint (str "http://" config/STATUS_BACKEND_SERVER_HOST "/statusgo/")
:signal-endpoint (str "ws://" config/STATUS_BACKEND_SERVER_HOST "/signals")
:root-data-dir config/STATUS_BACKEND_SERVER_ROOT_DATA_DIR})
:root-data-dir "/home/ulises/p/status-backend-data-dir/"})
(defn set-config!
[{:keys [server-enabled? status-go-endpoint signal-endpoint root-data-dir]}]
@@ -1,5 +1,6 @@
(ns status-im.subs.wallet.dapps.requests
(:require [re-frame.core :as rf]
[status-im.contexts.keycard.utils :as keycard]
[status-im.contexts.wallet.common.utils :as wallet-utils]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
@@ -59,3 +60,10 @@
:wallet-connect/typed-data-request?
:<- [:wallet-connect/current-request-method]
typed-data/typed-data-request?)
(rf/reg-sub
:wallet-connect/sign-on-keycard?
:<- [:wallet/keypairs]
:<- [:wallet-connect/current-request-address]
(fn [[keypairs address]]
(keycard/keycard-address? keypairs address)))
+177 -12
View File
@@ -1,9 +1,10 @@
import datetime
import re
import time
import pytest
from _pytest.outcomes import Failed
from selenium.common import NoSuchElementException
from selenium.common import NoSuchElementException, TimeoutException
from base_test_case import MultipleSharedDeviceTestCase, create_shared_drivers
from support.api.network_api import NetworkApi
@@ -144,7 +145,7 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
device_time_before_sending = self.wallet_1.driver.device_time
self.wallet_1.send_asset(address='arb1:' + self.receiver['wallet_address'],
asset_name='Ether',
amount=amount_to_send,
amount=f"{amount_to_send:.4f}",
network_name=self.network)
# ToDo: Arbiscan API is down, looking for analogue
# self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
@@ -180,7 +181,7 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
device_time_before_sending = self.wallet_1.driver.device_time
self.wallet_1.send_asset_from_drawer(address='arb1:' + self.receiver['wallet_address'],
asset_name='Ether',
amount=amount_to_send,
amount=f"{amount_to_send:.4f}",
network_name=self.network)
# ToDo: Arbiscan API is down, looking for analogue
# self.network_api.wait_for_confirmation_of_transaction(address=self.sender['wallet_address'],
@@ -210,13 +211,12 @@ class TestWalletMultipleDevice(MultipleSharedDeviceTestCase):
class TestWalletOneDevice(MultipleSharedDeviceTestCase):
def prepare_devices(self):
self.network_api = NetworkApi()
self.drivers, self.loop = create_shared_drivers(1)
self.sign_in_view = SignInView(self.drivers[0])
self.sender, self.receiver = transaction_senders['ETH_1'], transaction_senders['ETH_2']
self.total_balance = {'Ether': 0.0052, 'USDCoin': 5.0, 'Status': 10.0, 'Uniswap': 0.627, 'Dai Stablecoin': 0.0}
self.total_balance = {'Ether': 0.0062, 'USDCoin': 5.0, 'Status': 13.0, 'Uniswap': 0.627, 'Dai Stablecoin': 0.0}
self.mainnet_balance = {'Ether': 0.005, 'USDCoin': 0.0, 'Status': 10.0, 'Uniswap': 0.127, 'Dai Stablecoin': 0.0}
self.optimizm_balance = {'Ether': 0.0001, 'USDCoin': 5.0, 'Status': 0, 'Uniswap': 0, 'Dai Stablecoin': 0.0}
self.optimism_balance = {'Ether': 0.0011, 'USDCoin': 5.0, 'Status': 3.0, 'Uniswap': 0, 'Dai Stablecoin': 0.0}
self.arb_balance = {'Ether': 0.0001, 'USDCoin': 0.0, 'Status': 0.0, 'Uniswap': 0.5, 'Dai Stablecoin': 0.0}
self.sender['wallet_address'] = '0x' + self.sender['address']
self.receiver['wallet_address'] = '0x' + self.receiver['address']
@@ -224,15 +224,13 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
self.home_view = self.sign_in_view.get_home_view()
self.sender_username = self.home_view.get_username()
self.profile_view = self.home_view.profile_button.click()
self.profile_view.switch_network()
self.sign_in_view.sign_in(user_name=self.sender_username)
self.wallet_view = self.home_view.wallet_tab.click()
@marks.testrail_id(740490)
def test_wallet_balance_mainnet(self):
self.profile_view = self.home_view.profile_button.click()
self.profile_view.switch_network()
self.sign_in_view.sign_in(user_name=self.sender_username)
self.sign_in_view.wallet_tab.click()
self.wallet_view.just_fyi("Checking total balance")
real_balance = {}
for asset in self.total_balance:
@@ -245,7 +243,7 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
expected_balances = {
'Mainnet': self.mainnet_balance,
'Arbitrum': self.arb_balance,
'Optimism': self.optimizm_balance
'Optimism': self.optimism_balance
}
for network in expected_balances:
@@ -262,8 +260,175 @@ class TestWalletOneDevice(MultipleSharedDeviceTestCase):
self.errors.verify_no_errors()
@marks.testrail_id(741554)
def test_wallet_send_flow_mainnet(self):
self.wallet_view.get_account_element().click()
self.wallet_view.send_button.click()
self.wallet_view.address_text_input.send_keys(self.receiver['wallet_address'])
self.wallet_view.continue_button.click()
asset_data = {
'Ether': {'networks': ['Mainnet', 'Arbitrum'], 'amount': '0.00001'},
'Status': {'networks': ['Mainnet', 'Optimism'], 'amount': '1'}
}
for asset, data in asset_data.items():
for network in data['networks']:
self.wallet_view.just_fyi("Checking the send flow for %s on %s" % (asset, network))
self.wallet_view.select_asset(asset)
self.wallet_view.select_network(network)
self.wallet_view.set_amount(data['amount'])
try:
max_fees_text = self.wallet_view.get_data_item_element_text(data_item_name='Max fees')
if not re.findall(r"[$]\d+.\d+", max_fees_text):
self.errors.append(self.wallet_view,
"%s on %s: max fee is not a number - %s" % (asset, network, max_fees_text))
except TimeoutException:
self.errors.append(
self.wallet_view,
"%s on %s: max fees is not shown before Review Send button is clicked" % (asset, network))
self.wallet_view.confirm_button.click()
self.wallet_view.just_fyi("Checking Review Send page for %s on %s" % (asset, network))
expected_amount = "%s %s" % (data['amount'], 'ETH' if asset == 'Ether' else 'SNT')
sender_short_address = self.sender['wallet_address'].replace(self.sender['wallet_address'][6:-3],
'').lower()
receiver_short_address = self.receiver['wallet_address'].replace(self.receiver['wallet_address'][6:-3],
'').lower()
for text in ['Account 1', sender_short_address, expected_amount]:
if not self.wallet_view.from_data_container.get_child_element_by_text(text).is_element_displayed():
self.errors.append(
self.wallet_view,
"%s on %s: text %s is not shown in 'From' container on the Review Send page" % (
asset, network, text))
for text in [receiver_short_address, expected_amount]:
if not self.wallet_view.to_data_container.get_child_element_by_text(text).is_element_displayed():
self.errors.append(
self.wallet_view,
"%s on %s: text %s is not shown in 'To' container on the Review Send page" % (
asset, network, text))
data_to_check = {
'Est. time': ' min',
'Max fees': r"[$]\d+.\d+",
'Recipient gets': expected_amount
}
for key, expected_value in data_to_check.items():
try:
text = self.wallet_view.get_data_item_element_text(data_item_name=key)
if key == 'Max fees':
if not re.findall(expected_value, text):
self.errors.append(self.wallet_view,
"%s on %s: max fee is not a number - %s on the Review Send page" % (
asset, network, text))
else:
if text != expected_value:
self.errors.append(
self.wallet_view,
"%s on %s: %s text %s doesn't match expected %s on the Review Send page" % (
asset, network, key, text, expected_value))
except TimeoutException:
self.errors.append(self.wallet_view,
"%s on %s: %s is not shown on the Review Send page" % (asset, network, key))
self.wallet_view.slide_button_track.slide()
if not self.wallet_view.password_input.is_element_displayed():
self.errors.append("%s on %s: can't confirm transaction" % (asset, network))
self.wallet_view.click_system_back_button_until_presence_of_element(
element=self.wallet_view.element_by_text('Select token'), attempts=4)
self.wallet_view.click_system_back_button_until_presence_of_element(
element=self.wallet_view.add_account_button, attempts=6)
self.errors.verify_no_errors()
@marks.testrail_id(741555)
def test_wallet_swap_flow_mainnet(self):
self.wallet_view.navigate_back_to_wallet_view()
self.wallet_view.get_account_element().click()
self.wallet_view.swap_button.click()
for network in ['Mainnet', 'Optimism']:
self.wallet_view.just_fyi("Checking the Swap flow for SNT on %s" % network)
self.wallet_view.select_asset('Status')
self.wallet_view.select_network(network)
self.wallet_view.set_amount('1')
data_to_check = {
'Max fees': r"[$]\d+.\d+",
'Max slippage': r"\d+.\d+[%]"
}
for key, expected_value in data_to_check.items():
try:
text = self.wallet_view.get_data_item_element_text(data_item_name=key)
if not re.findall(expected_value, text):
self.errors.append(self.wallet_view,
"%s: %s is not a number - %s before pressing Review Swap button" % (
network, key, text))
except TimeoutException:
self.errors.append(self.wallet_view,
"%s: %s is not shown before pressing Review Swap button" % (network, key))
try:
self.wallet_view.wait_for_swap_input_to_be_shown()
except TimeoutException:
self.errors.append(self.wallet_view, "%s: ETH amount is not displayed")
continue
self.wallet_view.approve_swap_button.click()
self.wallet_view.just_fyi("Checking 'Set Spending Cap' screen for %s" % network)
if not self.wallet_view.spending_cap_approval_info_container.get_child_element_by_text(
'1 SNT').is_element_displayed():
self.errors.append(self.wallet_view,
"%s: Spending cap is not shown on the 'Set Spending Cap' screen" % network)
for text in ['Account 1',
self.sender['wallet_address'].replace(self.sender['wallet_address'][5:-3], '...').lower()]:
if not self.wallet_view.account_approval_info_container.get_child_element_by_text(
text).is_element_displayed():
self.errors.append(
self.wallet_view,
"%s: Text %s is not shown for the account on the 'Set Spending Cap' screen" % (network, text))
if not self.wallet_view.token_approval_info_container.get_child_element_by_text(
'SNT').is_element_displayed():
self.errors.append(self.wallet_view,
"%s: Token is not shown on the 'Set Spending Cap' screen" % network)
if not self.wallet_view.spender_contract_approval_info_container.is_element_displayed():
self.errors.append(self.wallet_view,
"%s: Spender contract info is missing on the 'Set Spending Cap' screen" % network)
data_to_check = {
'Network': network,
# 'Max fees': r"[$]\d+.\d+", # ToDO: enable when https://github.com/status-im/status-mobile/issues/21948 is fixed
# 'Est. time': ' min',
}
for key, expected_value in data_to_check.items():
try:
text = self.wallet_view.get_data_item_element_text(data_item_name=key)
if key == 'Max fees':
if not re.findall(expected_value, text):
self.errors.append(self.wallet_view,
"%s: max fee is not a number - %s on the 'Set Spending Cap' screen" % (
network, text))
else:
if text != expected_value:
self.errors.append(
self.wallet_view,
"%s: %s text %s doesn't match expected %s on the 'Set Spending Cap' screen" % (
network, key, text, expected_value))
except (TimeoutException, NoSuchElementException):
self.errors.append(self.wallet_view,
"%s: %s is not shown on the 'Set Spending Cap' screen" % (network, key))
try:
self.wallet_view.slide_button_track.slide()
if not self.wallet_view.password_input.is_element_displayed():
self.errors.append(self.wallet_view, "%s: can't sign swap" % network)
except NoSuchElementException:
self.errors.append(self.wallet_view, "%s: can't sign swap" % network)
self.wallet_view.click_system_back_button_until_presence_of_element(
element=self.wallet_view.element_by_text('Select asset to pay'), attempts=4)
self.wallet_view.click_system_back_button_until_presence_of_element(
element=self.wallet_view.add_account_button, attempts=6)
self.errors.verify_no_errors()
@marks.testrail_id(727231)
def test_wallet_add_remove_regular_account(self):
self.wallet_view.navigate_back_to_wallet_view()
self.wallet_view.just_fyi("Adding new regular account")
new_account_name = "New Account"
self.wallet_view.add_regular_account(account_name=new_account_name)
+4 -4
View File
@@ -201,7 +201,7 @@ class BaseView(object):
for _ in range(times):
self.driver.press_keycode(4)
def _navigate_back_to_view(self, element, attempts=3):
def click_system_back_button_until_presence_of_element(self, element, attempts=3):
counter = 0
while not element.is_element_displayed(1) and counter <= attempts:
self.driver.press_keycode(4)
@@ -218,14 +218,14 @@ class BaseView(object):
or not self.community_floating_screen.is_element_disappeared(1) \
or not self.discover_communities_floating_screen.is_element_disappeared(1):
self.driver.press_keycode(4)
self._navigate_back_to_view(self.chats_tab)
self.click_system_back_button_until_presence_of_element(self.chats_tab)
def navigate_back_to_chat_view(self):
self._navigate_back_to_view(self.get_chat_view().chat_message_input)
self.click_system_back_button_until_presence_of_element(self.get_chat_view().chat_message_input)
def navigate_back_to_wallet_view(self, attempts=3):
element = self.get_wallet_view().network_drop_down
self._navigate_back_to_view(element)
self.click_system_back_button_until_presence_of_element(element)
def click_system_home_button(self):
self.driver.info('Press system Home button')
+36 -4
View File
@@ -84,6 +84,7 @@ class WalletView(BaseView):
self.driver, xpath="//*[@content-desc='account-avatar']/../following-sibling::android.widget.TextView[1]")
self.account_emoji_button = Button(self.driver, accessibility_id='account-emoji')
self.send_button = Button(self.driver, accessibility_id='send')
self.swap_button = Button(self.driver, accessibility_id='swap')
self.send_from_drawer_button = Button(
self.driver, xpath="//*[@content-desc='send']/*[@content-desc='left-icon-for-action']")
self.copy_address_button = Button(self.driver, accessibility_id='copy-address')
@@ -104,6 +105,27 @@ class WalletView(BaseView):
self.confirm_button = Button(self.driver, accessibility_id='button-one')
self.done_button = Button(self.driver, accessibility_id='done')
# Review Send page
self.from_data_container = BaseElement(
self.driver, xpath="//*[@content-desc='summary-from-label']/following-sibling::android.view.ViewGroup[1]")
self.to_data_container = BaseElement(
self.driver, xpath="//*[@content-desc='summary-to-label']/following-sibling::android.view.ViewGroup[1]")
# Swap flow
self.approve_swap_button = Button(self.driver, accessibility_id='Approve')
self.spending_cap_approval_info_container = BaseElement(
self.driver,
xpath="//*[@content-desc='spending-cap-label']/following-sibling::*[@content-desc='approval-info'][1]")
self.account_approval_info_container = BaseElement(
self.driver,
xpath="//*[@content-desc='account-label']/following-sibling::*[@content-desc='approval-info'][1]")
self.token_approval_info_container = BaseElement(
self.driver,
xpath="//*[@content-desc='token-label']/following-sibling::*[@content-desc='approval-info'][1]")
self.spender_contract_approval_info_container = BaseElement(
self.driver,
xpath="//*[@content-desc='spender-contract-label']/following-sibling::*[@content-desc='approval-info'][1]")
# Edit key pair
self.edit_key_pair_button = Button(self.driver, accessibility_id="Edit")
self.key_pairs_plus_button = Button(self.driver, accessibility_id="standard-title-action")
@@ -156,11 +178,11 @@ class WalletView(BaseView):
self.confirm_button.click()
self.slide_and_confirm_with_password()
def set_amount(self, amount: float):
for i in '{:f}'.format(amount).rstrip('0'):
def set_amount(self, amount: str):
for i in amount:
Button(self.driver, accessibility_id='keyboard-key-%s' % i).click()
def send_asset(self, address: str, asset_name: str, amount: float, network_name: str):
def send_asset(self, address: str, asset_name: str, amount: str, network_name: str):
self.send_button.click()
self.address_text_input.send_keys(address)
self.continue_button.click()
@@ -169,7 +191,7 @@ class WalletView(BaseView):
self.set_amount(amount)
self.confirm_transaction()
def send_asset_from_drawer(self, address: str, asset_name: str, amount: float, network_name: str):
def send_asset_from_drawer(self, address: str, asset_name: str, amount: str, network_name: str):
asset_element = self.get_asset(asset_name)
asset_element.long_press_without_release()
self.send_from_drawer_button.double_click()
@@ -250,3 +272,13 @@ class WalletView(BaseView):
wallet_address = self.sharing_text_native.text
self.click_system_back_button()
return wallet_address
def get_data_item_element_text(self, data_item_name: str):
element = Text(self.driver, xpath="//*[@text='%s']/following-sibling::android.widget.TextView" % data_item_name)
element.wait_for_element(15)
return element.text
def wait_for_swap_input_to_be_shown(self):
locator = "//*[@content-desc='swap-input'][2]//*[@content-desc='token-avatar']" \
"/following-sibling::*//*[starts-with(@text,'0.000')]"
BaseElement(self.driver, xpath=locator).wait_for_visibility_of_element()
+1 -1
View File
@@ -884,7 +884,6 @@
"enable": "Enable",
"enable-access-to-camera": "Enable access to camera",
"enable-access-to-local-network": "Enable access to local network",
"enable-access-to-local-network-description": "To pair with your other device in the network",
"enable-all": "Enable all",
"enable-biometrics": "Enable biometrics",
"enable-camera": "Enable camera",
@@ -1382,6 +1381,7 @@
"keycard-connected-title": "Connected",
"keycard-contains-key-pair": "Keycard contains your profile key pair",
"keycard-desc": "Own a Keycard? Store your keys on it; youll need it for transactions",
"keycard-documentation": "Keycard is a hardware wallet in a credit-card sized smartcard that works with the Status app. You can store your Status keys and sign transactions securely with it.\n\nKeycard is designed to be mobile-friendly and uses contactless NFC technology to communicate with your phone, so you can sign transactions with just a tap of the card. It is lightweight, convenient, and can fit in your wallet, pocket, or phone case.\n\nProtect your assets and accounts with the Keycard, a cold storage hardware wallet that provides a contactless and convenient experience.",
"keycard-dont-ask-card": "Don't ask for card to sign in",
"keycard-empty": "Keycard is empty",
"keycard-empty-ready": "Keycard is empty and ready to be used",