Compare commits

...
40 changed files with 952 additions and 767 deletions
+15
View File
@@ -0,0 +1,15 @@
(ns status-im.common.promesa.core
(:require [promesa.core :as promesa]))
(defmacro all-seq
"A drop-in replacement for promesa/all which runs the promises in
sequence, instead of running them in parallel."
[bindings]
`(promesa/loop [results# []
remaining# ~bindings]
(if (empty? remaining#)
(promesa/resolved results#)
(promesa/let [result# (first remaining#)
value# (promesa/resolved result#)]
(promesa/recur (conj results# value#) (rest remaining#))))))
@@ -1,7 +1,9 @@
(ns status-im.common.standard-authentication.core
(:require
status-im.common.standard-authentication.password-input.view
status-im.common.standard-authentication.standard-auth.slide-button.view))
status-im.common.standard-authentication.standard-auth.slide-button.view
status-im.common.standard-authentication.utils))
(def password-input status-im.common.standard-authentication.password-input.view/view)
(def slide-button status-im.common.standard-authentication.standard-auth.slide-button.view/view)
(def transaction-payload status-im.common.standard-authentication.utils/transaction-payload)
@@ -1,93 +1,137 @@
(ns status-im.common.standard-authentication.events
(:require
[schema.core :as schema]
[status-im.common.keychain.events :as keychain]
[status-im.common.standard-authentication.enter-password.view :as enter-password]
[status-im.common.standard-authentication.events-schema :as events-schema]
[status-im.contexts.keycard.pin.view :as keycard.pin]
status-im.common.standard-authentication.keycard-events
[status-im.common.standard-authentication.utils :as utils]
[status-im.contexts.wallet.data-store :as data-store]
[taoensso.timbre :as log]
[utils.address]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn- handle-password-success
[has-partially-operable-accounts? on-auth-success masked-password]
(let [on-auth-success-callback #(on-auth-success masked-password)]
(rf/dispatch [:standard-auth/set-success true])
(rf/dispatch [:standard-auth/reset-login-password])
(if has-partially-operable-accounts?
(rf/dispatch [:wallet/make-partially-operable-accounts-fully-operable
{:password masked-password
:on-success on-auth-success-callback
:on-error on-auth-success-callback}])
(on-auth-success-callback))))
(defn authorize-and-sign
[{:keys [db]} [{:keys [sign-payload on-sign-success on-sign-error] :as args}]]
(let [keypairs (get-in db [:wallet :keypairs])
keycard-pairing? (utils/keycard-pairing? db)
keycard-keypair? (->> sign-payload
first
:address
(utils/keycard-address? keypairs))
keycard-signing? (and keycard-pairing? keycard-keypair?)
all-addresses-from-profile-keypair? (->> sign-payload
(map :address)
(utils/all-addresses-from-profile-keypair? keypairs))]
{:fx [(cond
;; NOTE: signing with both the keycard keypair and a non-keycard keypair (on-device)
;; is not yet supported, since it would require both scanning the keycard and entering
;; the password in the same flow
(and keycard-pairing?
(not all-addresses-from-profile-keypair?)) [:dispatch
[:keycard/feature-unavailable-show
{:theme :dark
:feature-name
:standard-auth/multiple-keypair-signing}]]
keycard-signing? [:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete (fn [pin]
(rf/dispatch
[:keycard/connect-and-sign-messages
{:keycard-pin pin
:messages sign-payload
:on-success (fn [signatures]
(rf/dispatch [:hide-bottom-sheet])
(on-sign-success signatures))
:on-failure on-sign-error}]))}]]
:else
[:dispatch
[:standard-auth/authorize
(assoc args
:on-auth-success
(fn [masked-password]
(rf/dispatch [:standard-auth/sign-on-device
{:masked-password masked-password
:sign-payload sign-payload
:on-success on-sign-success
:on-error on-sign-error}])))]])]}))
(schema/=> authorize-and-sign events-schema/?authorize-and-sign)
(rf/reg-event-fx :standard-auth/authorize-and-sign authorize-and-sign)
(defn sign-on-device
[_ [{:keys [masked-password sign-payload on-success on-error]}]]
{:fx [[:effects.wallet/sign-messages
{:messages sign-payload
:password masked-password
:on-success on-success
:on-error on-error}]]})
(rf/reg-event-fx :standard-auth/sign-on-device sign-on-device)
(defn authorize
[{:keys [db]} [{:keys [on-auth-success] :as args}]]
(let [key-uid (get-in db [:profile/profile :key-uid])
keycard-profile? (get-in db [:profile/profile :keycard-pairing])]
[{:keys [db]} [args]]
(let [keycard-pairing? (utils/keycard-pairing? db)
biometrics? (utils/biometrics-enabled? db)]
{:fx
[(if keycard-profile?
[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete
(fn [pin]
(rf/dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/get-keys
{:pin pin
:on-success (fn [{:keys [encryption-public-key]}]
(rf/dispatch [:keycard/disconnect])
(handle-password-success false
on-auth-success
(security/mask-data encryption-public-key)))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]))}]))}]]
[:effects.biometric/check-if-available
{:key-uid key-uid
:on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args])
:on-fail #(rf/dispatch [:standard-auth/authorize-with-password args])}])]}))
[(cond
biometrics? [:dispatch [:standard-auth/authorize-with-biometric args]]
keycard-pairing? [:dispatch [:standard-auth/authorize-with-keycard-key args]]
:else [:dispatch [:standard-auth/authorize-with-password args]])]}))
(schema/=> authorize events-schema/?authorize)
(rf/reg-event-fx :standard-auth/authorize authorize)
(defn authorize-with-biometric
[_ [{:keys [on-auth-success on-auth-fail on-close] :as args}]]
(let [args-with-biometric-btn
(assoc args
:on-press-biometric
#(rf/dispatch [:standard-auth/authorize-with-biometric args]))]
{:fx [[:dispatch [:dismiss-keyboard]]
[:dispatch
[:biometric/authenticate
{:prompt-message (i18n/label :t/biometric-auth-confirm-message)
:on-cancel #(rf/dispatch [:standard-auth/authorize-with-password
args-with-biometric-btn])
:on-success (fn []
(rf/dispatch [:standard-auth/on-biometric-success on-auth-success])
(rf/dispatch [:standard-auth/close on-close]))
:on-fail (fn [err]
(rf/dispatch [:standard-auth/authorize-with-password
args-with-biometric-btn])
(when on-auth-fail (on-auth-fail err))
(rf/dispatch [:standard-auth/on-biometric-fail err]))}]]]}))
[_ [{:keys [on-auth-success on-auth-fail] :as args}]]
{:fx [[:dispatch [:dismiss-keyboard]]
[:dispatch
[:biometric/authenticate
{:prompt-message (i18n/label :t/biometric-auth-confirm-message)
:on-cancel #(rf/dispatch [:standard-auth/fallback-for-biometrics args])
:on-success (fn []
(rf/dispatch [:standard-auth/on-biometric-success on-auth-success]))
:on-fail (fn [err]
(rf/dispatch [:standard-auth/fallback-for-biometrics args])
(when on-auth-fail (on-auth-fail err))
(rf/dispatch [:standard-auth/on-biometric-fail err]))}]]]})
(defn fallback-for-biometrics
[{:keys [db]} [args]]
(let [keycard-pairing? (utils/keycard-pairing? db)]
{:fx [(if keycard-pairing?
[:dispatch [:standard-auth/authorize-with-keycard-key args]]
[:dispatch [:standard-auth/authorize-with-password args]])]}))
(rf/reg-event-fx :standard-auth/fallback-for-biometrics fallback-for-biometrics)
(schema/=> authorize-with-biometric events-schema/?authorize-with-biometric)
(rf/reg-event-fx :standard-auth/authorize-with-biometric authorize-with-biometric)
(defn on-biometric-success
[{:keys [db]} [on-auth-success]]
(defn get-keychain-key
[{:keys [db]} [on-done]]
(let [key-uid (get-in db [:profile/profile :key-uid])
keycard? (get-in db [:profile/profile :keycard-pairing])]
keycard? (utils/keycard-pairing? db)]
{:fx [(if keycard?
[:keychain/get-keycard-keys [key-uid on-auth-success]]
[:keychain/get-user-password [key-uid on-auth-success]])
[:dispatch [:standard-auth/set-success true]]
[:dispatch [:standard-auth/reset-login-password]]]}))
[:keychain/get-keycard-keys [key-uid on-done]]
[:keychain/get-user-password [key-uid on-done]])]}))
(rf/reg-event-fx :standard-auth/get-keychain-key get-keychain-key)
(defn on-biometric-success
[_ [on-auth-success]]
{:fx [[:dispatch
[:standard-auth/get-keychain-key
(fn [masked-key]
(rf/dispatch [:standard-auth/finish-auth
{:on-auth-success on-auth-success
:masked-password masked-key}]))]]]})
(schema/=> on-biometric-success events-schema/?on-biometric-success)
(rf/reg-event-fx :standard-auth/on-biometric-success on-biometric-success)
@@ -106,29 +150,19 @@
(rf/reg-event-fx :standard-auth/on-biometric-fail on-biometric-fail)
(defn- bottom-sheet-password-view
[{:keys [on-press-biometric on-auth-success auth-button-icon-left auth-button-label]}]
[{:keys [on-auth-success auth-button-icon-left auth-button-label skip-biometrics?] :as args}]
(fn []
(let [has-partially-operable-accounts? (rf/sub [:wallet/has-partially-operable-accounts?])]
(let [auth-method (rf/sub [:auth-method])
biometric-enabled? (= auth-method keychain/auth-method-biometric)]
[enter-password/view
{:on-enter-password #(handle-password-success
has-partially-operable-accounts?
on-auth-success
(security/hash-masked-password %))
:on-press-biometrics on-press-biometric
{:on-enter-password #(rf/dispatch [:standard-auth/finish-auth
{:masked-password (security/hash-masked-password %)
:on-auth-success on-auth-success}])
:on-press-biometrics (when (and (not skip-biometrics?) biometric-enabled?)
#(rf/dispatch [:standard-auth/authorize-with-biometric args]))
:button-icon-left auth-button-icon-left
:button-label auth-button-label}])))
(defn authorize-with-keycard
[_ [{:keys [on-complete]}]]
{:fx [[:dispatch
[:show-bottom-sheet
{:hide-on-background-press? false
:on-close #(rf/dispatch [:standard-auth/reset-login-password])
:content (fn []
[keycard.pin/auth {:on-complete on-complete}])}]]]})
(rf/reg-event-fx :standard-auth/authorize-with-keycard authorize-with-keycard)
(defn authorize-with-password
[_ [{:keys [on-close theme blur?] :as args}]]
{:fx [[:dispatch [:standard-auth/reset-login-password]]
@@ -168,3 +202,28 @@
:standard-auth/set-success
(fn [{:keys [db]} [success?]]
{:db (assoc-in db [:profile/login :success?] success?)}))
(defn- finish-auth
[{:keys [db]} [{:keys [masked-password on-auth-success]}]]
(let [on-auth-success-callback #(on-auth-success masked-password)
has-partially-operable-accounts? (-> (get-in db [:wallet :accounts])
data-store/partially-operable-accounts?)
keycard-profile? (-> (get-in db [:wallet :keypairs])
utils/profile-keypair-on-keycard?)]
{:fx [[:dispatch [:standard-auth/set-success true]]
[:dispatch [:standard-auth/reset-login-password]]
(if (and has-partially-operable-accounts? (not keycard-profile?))
[:dispatch
[:wallet/make-partially-operable-accounts-fully-operable
{:password masked-password
:on-success on-auth-success-callback
:on-error on-auth-success-callback}]]
[:effects.standard-auth/on-auth-success on-auth-success-callback])]}))
(rf/reg-event-fx :standard-auth/finish-auth finish-auth)
(rf/reg-fx
:effects.standard-auth/on-auth-success
(fn [on-auth-success]
(when on-auth-success
(on-auth-success))))
@@ -10,6 +10,29 @@
[:blur? {:optional true} [:maybe boolean?]]
[:theme {:optional true} [:maybe :schema.common/theme]]])
(def ^:private ?sign-payload
[:sequential {:min 1}
[:map {:closed true}
[:message :string]
[:address :string]]])
(def ?authorize-and-sign
[:=>
[:catn
[:cofx :schema.re-frame/cofx]
[:args
[:tuple
[:map {:closed true}
[:sign-payload ?sign-payload]
[:on-sign-success fn?]
[:on-sign-error {:optional true} fn?]
[:on-close {:optional true} [:maybe fn?]]
[:auth-button-label {:optional true} [:maybe string?]]
[:auth-button-icon-left {:optional true} [:maybe keyword?]]
[:blur? {:optional true} [:maybe boolean?]]
[:theme {:optional true} [:maybe :schema.common/theme]]]]]]
:any])
(def ?authorize
[:=>
[:catn
@@ -49,5 +72,9 @@
[:catn
[:cofx :schema.re-frame/cofx]
[:args
[:tuple ?authorize-map]]]
[:tuple
[:merge
?authorize-map
[:map {:closed true}
[:skip-biometrics? {:optional true} boolean?]]]]]]
:any])
@@ -0,0 +1,93 @@
(ns status-im.common.standard-authentication.events-test
(:require [cljs.test :refer [deftest is are testing]]
matcher-combinators.test
[status-im.common.standard-authentication.events :as sut]))
(def profile-keypair
{:key-uid "profile-key-uid"
:name "profile"
:type :profile
:lowest-operability :fully
:accounts [{:path "m/44'/60'/0'/0/0"
:key-uid "profile-key-uid"
:address "0x1"
:wallet true
:type "generated"
:operable :fully}
{:path "m/44'/60'/0'/0/1"
:key-uid "profile-key-uid"
:address "0x2"
:wallet true
:type "generated"
:operable :fully}]})
(def second-keypair
{:key-uid "second-key-uid"
:name "second"
:type :seed
:lowest-operability :fully
:accounts [{:path "m/44'/60'/0'/0/0"
:key-uid "second-key-uid"
:address "0x3"
:wallet true
:type "generated"
:operable :fully}]})
(def default-cofx
{:db {:wallet {:keypairs {"profile-key-uid" profile-keypair}}}})
(deftest authorize-and-sign-test
(testing "sign-payload not passing schema validation"
(are [args] (thrown? js/Error (sut/authorize-and-sign default-cofx [args]))
{:sign-payload []
:on-sign-success identity}
{:sign-payload [{:address "0x0"}]
:on-sign-success identity}
{:sign-payload [{:message "0x0"}]
:on-sign-success identity}
{:sign-payload [{:message "0x0"
:address "0x1"}]}
{:on-sign-success identity}))
(testing "sign-payload is passing schema validation"
(let [args {:sign-payload [{:message "0x0" :address "0x1"}
{:message "0x0" :address "0x2"}]
:on-sign-success identity}]
(is (match? {} (sut/authorize-and-sign default-cofx [args])))))
(testing "signing-payload with addresses from multiple keypairs is unsupported for keycard"
(let [cofx {:db {:wallet {:keypairs {"profile-key-uid" profile-keypair
"second-key-uid" second-keypair}}
:profile/profile {:keycard-pairing "pairing-key"}}}
args {:sign-payload [{:message "0x0" :address "0x1"}
{:message "0x0" :address "0x2"}
{:message "0x0" :address "0x3"}]
:on-sign-success identity}
expected {:fx [[:dispatch [:keycard/feature-unavailable-show {}]]]}]
(is (match? expected (sut/authorize-and-sign cofx [args])))))
(testing "signing with the keycard"
(let [keycard-keypair (assoc profile-keypair :keycards [{:key-uid "profile-key-uid"}])
cofx {:db {:wallet {:keypairs {"profile-key-uid" keycard-keypair}}
:profile/profile {:keycard-pairing "pairing-key"}}}
args {:sign-payload [{:message "0x0" :address "0x1"}
{:message "0x0" :address "0x2"}]
:on-sign-success identity}
expected {:fx [[:dispatch [:standard-auth/authorize-with-keycard {}]]]}]
(is (match? expected (sut/authorize-and-sign cofx [args])))))
(testing "signing locally with encryption key stored on keycard"
(let [cofx {:db {:wallet {:keypairs {"profile-key-uid" profile-keypair}}
:profile/profile {:keycard-pairing "pairing-key"}}}
args {:sign-payload [{:message "0x0" :address "0x1"}
{:message "0x0" :address "0x2"}]
:on-sign-success identity}
expected {:fx [[:dispatch [:standard-auth/authorize {}]]]}]
(is (match? expected (sut/authorize-and-sign cofx [args])))))
(testing "signing locally with password/biometric"
(let [args {:sign-payload [{:message "0x0" :address "0x1"}
{:message "0x0" :address "0x2"}]
:on-sign-success identity}
expected {:fx [[:dispatch [:standard-auth/authorize {}]]]}]
(is (match? expected (sut/authorize-and-sign default-cofx [args]))))))
@@ -0,0 +1,54 @@
(ns status-im.common.standard-authentication.keycard-events
(:require [status-im.contexts.keycard.pin.view :as keycard.pin]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn authorize-with-keycard
[_ [{:keys [on-complete]}]]
{:fx [[:dispatch
[:show-bottom-sheet
{:hide-on-background-press? false
:on-close #(rf/dispatch [:standard-auth/reset-login-password])
:content (fn []
[keycard.pin/auth {:on-complete on-complete}])}]]]})
(rf/reg-event-fx :standard-auth/authorize-with-keycard authorize-with-keycard)
(defn- authorize-with-keycard-key
[{:keys [db]} [{:keys [on-auth-success]}]]
(let [key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete
(fn [pin]
(rf/dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/get-keys
{:pin pin
:on-success #(rf/dispatch [:standard-auth/on-keycard-key-success %
on-auth-success])
:on-failure #(rf/dispatch [:standard-auth/on-keycard-key-fail])}]))}]))}]]]}))
(rf/reg-event-fx :standard-auth/authorize-with-keycard-key authorize-with-keycard-key)
(defn on-keycard-key-success
[_ [encryption-key on-auth-success]]
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch
[:standard-auth/finish-auth
{:on-auth-success on-auth-success
:masked-password (security/mask-data encryption-key)}]]]})
(rf/reg-event-fx :standard-auth/on-keycard-key-success on-keycard-key-success)
(defn on-keycard-key-fail
[_ [error on-auth-fail]]
{:fx [[:dispatch [:keycard/on-action-with-pin-error error]]
[:effects.standard-auth/on-keycard-key-fail error on-auth-fail]]})
(rf/reg-event-fx :standard-auth/on-keycard-key-fail on-keycard-key-fail)
(rf/reg-fx :effects.standard-auth/on-keycard-key-fail
(fn [error on-auth-fail]
(when on-auth-fail
(on-auth-fail error))))
@@ -3,43 +3,47 @@
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im.common.biometric.utils :as biometric]
[status-im.constants :as constants]
[utils.re-frame :as rf]))
(defn view
[{:keys [track-text customization-color auth-button-label on-auth-success on-auth-fail
auth-button-icon-left size blur? container-style disabled? dependencies on-complete]
auth-button-icon-left size blur? container-style disabled? dependencies
sign-payload]
:or {container-style {:flex 1}}}]
(let [theme (quo.theme/use-theme)
auth-method (rf/sub [:auth-method])
biometric-auth? (= auth-method constants/auth-method-biometric)
slider-icon (rf/sub [:standard-auth/slider-icon])
on-complete-callback (rn/use-callback
(fn []
(rf/dispatch [:standard-auth/authorize
{:auth-button-icon-left auth-button-icon-left
:theme theme
:blur? blur?
:biometric-auth? biometric-auth?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label}]))
(vec (conj dependencies on-auth-success on-auth-fail)))
(if (seq sign-payload)
(rf/dispatch [:standard-auth/authorize-and-sign
{:sign-payload sign-payload
:theme theme
:blur? blur?
:on-sign-success on-auth-success
:on-sign-error on-auth-fail
:auth-button-label auth-button-label
:auth-button-icon-left auth-button-icon-left}])
(rf/dispatch [:standard-auth/authorize
{:theme theme
:blur? blur?
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label auth-button-label
:auth-button-icon-left auth-button-icon-left}])))
(vec (conj dependencies
on-auth-success
on-auth-fail
sign-payload)))
on-slider-complete (rn/use-callback
(fn [reset-slider-fn]
(js/setTimeout #(reset-slider-fn false) 500)
(if (fn? on-complete)
(on-complete)
(on-complete-callback)))
[on-complete on-complete-callback])
biometric-type (rf/sub [:biometrics/supported-type])]
(on-complete-callback))
[on-complete-callback])]
[quo/slide-button
{:container-style container-style
:size size
:customization-color customization-color
:on-complete on-slider-complete
:track-icon (if biometric-auth?
(biometric/get-icon-by-type biometric-type)
:password)
:track-icon slider-icon
:track-text track-text
:disabled? disabled?}]))
@@ -0,0 +1,58 @@
(ns status-im.common.standard-authentication.utils
(:require [clojure.string :as string]
[status-im.common.keychain.events :as keychain]))
(defn keycard-pairing?
[db]
(boolean (get-in db [:profile/profile :keycard-pairing])))
(defn profile-keypair-keycards?
[{:keys [type keycards]}]
(and (= :profile type) (seq keycards)))
(defn keycard-address?
[keypairs address]
(let [find-keycard-keypair (fn [kps] (some #(when (profile-keypair-keycards? %) %) kps))
keypair-addresses (fn [kp]
(->> (:accounts kp)
(map :address)
set))]
(-> keypairs
vals
find-keycard-keypair
keypair-addresses
(contains? (string/lower-case address)))))
(defn profile-keypair-on-keycard?
[keypairs]
(->> keypairs
vals
(some #(when (profile-keypair-keycards? %) %))
boolean))
(defn all-addresses-from-profile-keypair?
[keypairs addresses]
(let [profile-keypair (->> keypairs
vals
(some #(when (= (:type %) :profile) %)))
profile-addresses (->> profile-keypair
:accounts
(map :address)
set)]
(every? #(contains? profile-addresses %) addresses)))
(defn biometrics-enabled?
[db]
(= (:auth-method db) keychain/auth-method-biometric))
(defn transaction-payload
[transaction-for-signing]
(let [signing-details (:signingDetails transaction-for-signing)
address (:address signing-details)
messages (:hashes signing-details)]
(reduce (fn [acc message]
(conj acc
{:address address
:message message}))
[]
messages)))
-1
View File
@@ -104,7 +104,6 @@
(def fast-create-community-enabled?
(enabled? (get-config :FAST_CREATE_COMMUNITY_ENABLED "0")))
(def community-accounts-selection-enabled? true)
(def fetch-messages-enabled? (enabled? (get-config :FETCH_MESSAGES_ENABLED "1")))
(def test-networks-enabled? (enabled? (get-config :TEST_NETWORKS_ENABLED "0")))
@@ -1,108 +1,99 @@
(ns status-im.contexts.communities.actions.accounts-selection.effects
(:require
[clojure.string :as string]
[promesa.core :as promesa]
[schema.core :as schema]
[status-im.common.json-rpc.events :as rpc]
[utils.re-frame :as rf]
[utils.security.core :as security]))
[utils.re-frame :as rf]))
(def ^:private ?addresses-to-reveal
[:or [:set string?]
[:sequential string?]])
(defn- generate-requests-for-signing
[pub-key community-id addresses-to-reveal]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
{:method :wakuext_generateJoiningCommunityRequestsForSigning
:params [pub-key community-id addresses-to-reveal]
:on-success p-resolve
:on-error #(p-reject (str "failed to generate requests for signing\n" %))}))))
(rpc/call-async :wakuext_generateJoiningCommunityRequestsForSigning
false
pub-key
community-id
(or addresses-to-reveal [])))
(defn- sign-data
[sign-params password]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
{:method :wakuext_signData
:params [(map #(assoc % :password (security/safe-unmask-data password)) sign-params)]
:on-success p-resolve
:on-error #(p-reject (str "failed to sign data\n" %))}))))
(schema/=> generate-requests-for-signing
[:=>
[:catn
[:community-id string?]
[:pub-key string?]
[:addresses-to-reveal ?addresses-to-reveal]]
:any])
(rf/reg-fx
:effects.community/generate-requests-for-signing
(fn [{:keys [pub-key community-id addresses-to-reveal on-success on-error]}]
(-> (generate-requests-for-signing pub-key community-id addresses-to-reveal)
(promesa/then #(map (fn [request]
{:message (:data request)
:address (-> request :account string/lower-case)})
%))
(promesa/then on-success)
(promesa/catch on-error))))
(defn- edit-shared-addresses-for-community
[community-id signatures addresses-to-reveal airdrop-address _share-future-addresses?]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
{:method :wakuext_editSharedAddressesForCommunity
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address}]
:js-response true
:on-success p-resolve
:on-error p-reject}))))
(rpc/call-async :wakuext_editSharedAddressesForCommunity
true
{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address}))
(defn- request-to-join
[community-id signatures addresses-to-reveal airdrop-address share-future-addresses?]
(promesa/create
(fn [p-resolve p-reject]
(rpc/call
{:method :wakuext_requestToJoinCommunity
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address
:shareFutureAddresses share-future-addresses?}]
:js-response true
:on-success p-resolve
:on-error p-reject}))))
(defn- run-callback-or-event
[callback-or-event result]
(cond (fn? callback-or-event)
(callback-or-event result)
(vector? callback-or-event)
(rf/dispatch (conj callback-or-event result))))
(defn- sign-and-call-endpoint
[{:keys [community-id password pub-key
addresses-to-reveal airdrop-address share-future-addresses?
on-success on-error
callback]}]
(-> (promesa/let [sign-params (generate-requests-for-signing pub-key
community-id
addresses-to-reveal)
signatures (sign-data sign-params password)
result (callback community-id
signatures
addresses-to-reveal
airdrop-address
share-future-addresses?)]
(run-callback-or-event on-success result))
(promesa/catch #(run-callback-or-event on-error %))))
(schema/=> sign-and-call-endpoint
(schema/=> edit-shared-addresses-for-community
[:=>
[:cat
[:map {:closed true}
[:community-id string?]
[:password [:maybe [:or string? security/?masked-password]]]
[:pub-key string?]
[:addresses-to-reveal
[:or [:set string?]
[:sequential string?]]]
[:airdrop-address string?]
[:share-future-addresses? boolean?]
[:on-success [:or fn? :schema.re-frame/event]]
[:on-error [:or fn? :schema.re-frame/event]]
[:callback fn?]]]
[:catn
[:community-id string?]
[:signatures [:sequential string?]]
[:addresses-to-reveal ?addresses-to-reveal]
[:airdrop-address string?]
[:_share-future-addresses? [:maybe boolean?]]]
:any])
(rf/reg-fx :effects.community/edit-shared-addresses
(fn [opts]
(sign-and-call-endpoint
(assoc opts :callback edit-shared-addresses-for-community))))
(fn [{:keys [on-success on-error community-id signatures addresses-to-reveal airdrop-address
share-future-addresses?]}]
(-> (edit-shared-addresses-for-community community-id
signatures
addresses-to-reveal
airdrop-address
share-future-addresses?)
(promesa/then on-success)
(promesa/catch on-error))))
(defn- request-to-join
[community-id signatures addresses-to-reveal airdrop-address share-future-addresses?]
(rpc/call-async :wakuext_requestToJoinCommunity
true
{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address
:shareFutureAddresses share-future-addresses?}))
(schema/=> request-to-join
[:=>
[:catn
[:community-id string?]
[:signatures [:sequential string?]]
[:addresses-to-reveal ?addresses-to-reveal]
[:airdrop-address string?]
[:share-future-addresses? [:maybe boolean?]]]
:any])
(rf/reg-fx :effects.community/request-to-join
(fn [opts]
(sign-and-call-endpoint
(assoc opts :callback request-to-join))))
(fn [{:keys [on-success on-error community-id signatures addresses-to-reveal airdrop-address
share-future-addresses?]}]
(-> (request-to-join community-id
signatures
addresses-to-reveal
airdrop-address
share-future-addresses?)
(promesa/then on-success)
(promesa/catch on-error))))
@@ -3,6 +3,7 @@
status-im.contexts.communities.actions.accounts-selection.effects
[status-im.contexts.communities.utils :as utils]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn initialize-permission-addresses
@@ -49,7 +50,7 @@
(rf/reg-event-fx :communities/do-init-permission-addresses do-init-permission-addresses)
(defn edit-shared-addresses
(defn sign-shared-addresses
"This event will effectively persist the choice of addresses and airdrop address
in status-go. It can either take addresses to share AND an airdrop address, or
just addresses to share OR an airdrop address. This is because when the user
@@ -57,24 +58,50 @@
choice of addresses to share, and vice-versa. If we omit addresses to share,
status-go will default to all available."
[{:keys [db]}
[{:keys [community-id password on-success addresses airdrop-address share-future-addresses?]}]]
[{:keys [community-id addresses]} :as args]]
(let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (if (seq addresses)
(set addresses)
(get-in db [:communities/all-addresses-to-reveal community-id]))]
{:fx [[:effects.community/generate-requests-for-signing
{:pub-key pub-key
:community-id community-id
:addresses-to-reveal addresses-to-reveal
:on-success (fn [signing-requests]
(rf/dispatch
[:standard-auth/authorize-and-sign
{:sign-payload signing-requests
:on-sign-success #(rf/dispatch
[:communities/edit-shared-addresses
(assoc args :signature-data %)])
:on-sign-error #(rf/dispatch
[:communities/edit-shared-addresses-failure
community-id %])
:auth-button-label (i18n/label :t/confirm-changes)}]))
:on-error (fn [error]
(rf/dispatch [:communities/edit-shared-addresses-failure community-id
error]))}]]}))
(rf/reg-event-fx :communities/sign-shared-addresses sign-shared-addresses)
(defn edit-shared-addresses
[{:keys [db]}
[{:keys [community-id signature-data airdrop-address share-future-addresses? on-success]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses-to-reveal (if (seq addresses)
(set addresses)
(get-in db [:communities/all-addresses-to-reveal community-id]))
addresses-to-reveal (set (map :address signature-data))
new-airdrop-address (if (contains? addresses-to-reveal airdrop-address)
airdrop-address
(->> wallet-accounts
(->> (utils/sorted-operable-non-watch-only-accounts db)
(filter #(contains? addresses-to-reveal (:address %)))
first
:address))
share-future-addresses? (if (nil? share-future-addresses?)
(get-in db [:communities/selected-share-all-addresses community-id])
share-future-addresses?)]
share-future-addresses?)
signatures (utils/extract-join-request-signatures signature-data)]
{:fx [[:effects.community/edit-shared-addresses
{:community-id community-id
:password password
:signatures signatures
:pub-key pub-key
:addresses-to-reveal addresses-to-reveal
:share-future-addresses? share-future-addresses?
@@ -85,8 +112,11 @@
new-airdrop-address
share-future-addresses?))
(rf/dispatch [:communities/edit-shared-addresses-success
community-id addresses-to-reveal airdrop-address]))
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}))
community-id addresses-to-reveal
airdrop-address]))
:on-error (fn [error]
[:communities/edit-shared-addresses-failure community-id
error])}]]}))
(rf/reg-event-fx :communities/edit-shared-addresses edit-shared-addresses)
@@ -99,7 +129,7 @@
(rf/reg-event-fx :communities/edit-shared-addresses-failure
(fn [{:keys [db]} [community-id error]]
(log/error "failed to edit shared addresses"
{:event :communities/edit-shared-addresses
{:event :communities/sign-shared-addresses
:community-id community-id
:error error})
{:db (assoc-in db [:password-authentication :error] error)}))
@@ -89,68 +89,90 @@
addresses-to-reveal]]]}
(sut/do-init-permission-addresses cofx [community-id revealed-accounts true]))))))
(deftest sign-shared-addresses-test
(testing
"when addresses to reveal is not passed, fallback to all wallet addresses"
(let [pub-key "abcdef"
revealed-addresses #{"0xB" "0xC"}
share-future-addresses? true
cofx {:db {:profile/profile {:public-key pub-key}
:communities/all-addresses-to-reveal {community-id
revealed-addresses}}}
airdrop-address "0xB"
actual (sut/sign-shared-addresses
cofx
[{:community-id community-id
:airdrop-address airdrop-address
:share-future-addresses? share-future-addresses?
:on-success identity}])]
(is (match?
{:fx [[:effects.community/generate-requests-for-signing
{:community-id community-id
:pub-key pub-key
:addresses-to-reveal revealed-addresses
:on-success fn?
:on-error fn?}]]}
actual)))))
(deftest edit-shared-addresses-test
(testing
"when airdrop address is passed, but addresses to reveal is not, then
fallback to all wallet addresses"
"signatures and addresses should be derived from signature-data and if airdrop address is not passed,
should fallback to first operable wallet address."
(let [pub-key "abcdef"
revealed-addresses #{"0xB" "0xC"}
share-future-addresses? true
cofx {:db {:profile/profile {:public-key pub-key}
:communities/all-addresses-to-reveal {community-id revealed-addresses}}}
airdrop-address "0xB"
cofx {:db {:profile/profile {:public-key pub-key}
:wallet {:accounts wallet-accounts}
;; 0xA is watch-only
:communities/all-addresses-to-reveal {community-id #{"0xA" "0xB" "0xC"}}}}
actual
(sut/edit-shared-addresses
cofx
[{:community-id community-id
:password password
:airdrop-address airdrop-address
:share-future-addresses? share-future-addresses?
:on-success (fn [new-addresses-to-reveal]
(is (match? revealed-addresses new-addresses-to-reveal)))}])
on-success-wrapper (-> actual :fx first second :on-success)]
actual (sut/edit-shared-addresses
cofx
[{:community-id community-id
:share-future-addresses? true
:signature-data [{:address "0xB"
:signature "sigB"
:message "mesB"}
{:address "0xC"
:signature "sigC"
:message "mesC"}]
:on-success identity}])]
(is (match?
{:fx [[:effects.community/edit-shared-addresses
{:community-id community-id
:password password
:signatures '("0xsigB" "0xsigC")
:pub-key pub-key
:addresses-to-reveal revealed-addresses
:share-future-addresses? share-future-addresses?
:airdrop-address airdrop-address
:addresses-to-reveal #{"0xB" "0xC"}
:share-future-addresses? true
:airdrop-address "0xB"
:on-success fn?
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}
actual))
:on-error fn?}]]}
actual))))
(on-success-wrapper)))
(testing "when addresses to reveal are passed, but airdrop address is not"
(testing "if share-future-addresses flag is not passed, should use the db value"
(let [pub-key "abcdef"
cofx {:db {:profile/profile {:public-key pub-key}
:wallet {:accounts wallet-accounts}
:communities/all-addresses-to-reveal {community-id #{"0xB" "0xC"}}}}
cofx {:db {:profile/profile {:public-key pub-key}
:wallet {:accounts wallet-accounts}
:communities/selected-share-all-addresses {community-id false}
:communities/all-addresses-to-reveal {community-id #{"0xA" "0xB"}}}}
actual (sut/edit-shared-addresses
cofx
[{:community-id community-id
:password password
:addresses ["0xC"]
:on-success (fn [new-addresses-to-reveal new-airdrop-address]
(is (= #{"0xC"} new-addresses-to-reveal))
(is (= "0xC" new-airdrop-address)))}])
on-success-wrapper
(-> actual :fx first second :on-success)]
actual (sut/edit-shared-addresses
cofx
[{:community-id community-id
:airdrop-address "0xB"
:signature-data [{:address "0xA"
:signature "sigA"
:message "mesA"}
{:address "0xB"
:signature "sigB"
:message "mesB"}]
:on-success identity}])]
(is (match?
{:fx [[:effects.community/edit-shared-addresses
{:community-id community-id
:password password
:pub-key pub-key
:addresses-to-reveal #{"0xC"}
:airdrop-address "0xC"
:on-success fn?
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}
actual))
(on-success-wrapper))))
{:community-id community-id
:signatures '("0xsigA" "0xsigB")
:pub-key pub-key
:addresses-to-reveal #{"0xA" "0xB"}
:share-future-addresses? false
:airdrop-address "0xB"
:on-success fn?
:on-error fn?}]]}
actual)))))
@@ -15,6 +15,7 @@
(defn bottom-actions
[theme]
{:padding-vertical 12
{:height 48
:margin-vertical 12
:padding-horizontal screen-horizontal-padding
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})
@@ -4,6 +4,7 @@
[quo.theme]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.communities.actions.accounts-selection.style :as style]
[status-im.contexts.communities.actions.addresses-for-permissions.view :as addresses-for-permissions]
[status-im.contexts.communities.actions.airdrop-addresses.view :as airdrop-addresses]
@@ -14,69 +15,88 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn navigate-back
[]
(rf/dispatch [:navigate-back]))
(defn- bottom-authentication
[{:keys [id]}]
(let [theme (quo.theme/use-theme)
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
{:keys [color]} (rf/sub [:communities/community id])
join-requests-for-signing (rf/sub [:communities/join-requests-for-signing-by-id id])
confirm-choices (rn/use-callback
(fn [signature-data]
(rf/dispatch [:communities/request-to-join
{:community-id id
:signature-data signature-data}])
(navigate-back))
[can-edit-addresses? id])]
(rn/use-effect
#(when (and (not can-edit-addresses?) (seq revealed-accounts))
(rf/dispatch [:communities/generate-requests-for-signing id]))
[can-edit-addresses? revealed-accounts])
(when-not can-edit-addresses?
[rn/view {:style (style/bottom-actions theme)}
[standard-auth/slide-button
{:sign-payload join-requests-for-signing
:disabled? (not (seq join-requests-for-signing))
:size :size-48
:track-text (i18n/label :t/slide-to-request-to-join)
:auth-button-label (if can-edit-addresses?
(i18n/label :t/edit-shared-addresses)
(i18n/label :t/request-to-join))
:customization-color color
:on-auth-success confirm-choices}]])))
(defn view
[]
(let [theme (quo.theme/use-theme)
{id :community-id} (rf/sub [:get-screen-params])
{:keys [name color images joined]} (rf/sub [:communities/community id])
has-permissions? (rf/sub [:communities/has-permissions? id])
airdrop-account (rf/sub [:communities/airdrop-account id])
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
revealed-accounts-count (count revealed-accounts)
wallet-accounts-count (count (rf/sub [:wallet/operable-accounts]))
addresses-shared-text (if (= revealed-accounts-count wallet-accounts-count)
(i18n/label :t/all-addresses)
(i18n/label-pluralize
revealed-accounts-count
:t/address-count))
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label (communities.utils/role->translation-key
highest-permission-role
:t/member))
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
navigate-back (rn/use-callback #(rf/dispatch [:navigate-back]))
show-addresses-for-permissions (rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :addresses-for-permissions
{:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content
addresses-for-permissions/view}])))
[can-edit-addresses?])
show-airdrop-addresses (rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :address-for-airdrop
{:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content airdrop-addresses/view}])))
[can-edit-addresses?])
confirm-choices (rn/use-callback
(fn []
(rf/dispatch
[:standard-auth/authorize
{:auth-button-label (if can-edit-addresses?
(i18n/label
:t/edit-shared-addresses)
(i18n/label :t/request-to-join))
:on-auth-success
(fn [password]
(rf/dispatch
[:communities/request-to-join-with-addresses
{:community-id id
:password password}]))}])
(navigate-back))
[can-edit-addresses?])
open-permission-sheet (rn/use-callback
(fn []
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[permissions-sheet/view id])}]))
[id])]
(let [{id :community-id} (rf/sub [:get-screen-params])
{:keys [name images joined]} (rf/sub [:communities/community id])
has-permissions? (rf/sub [:communities/has-permissions? id])
airdrop-account (rf/sub [:communities/airdrop-account id])
revealed-accounts (rf/sub [:communities/accounts-to-reveal id])
revealed-accounts-count (count revealed-accounts)
wallet-accounts-count (count (rf/sub [:wallet/operable-accounts]))
addresses-shared-text (if (= revealed-accounts-count wallet-accounts-count)
(i18n/label :t/all-addresses)
(i18n/label-pluralize
revealed-accounts-count
:t/address-count))
{:keys [highest-permission-role]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label (communities.utils/role->translation-key
highest-permission-role
:t/member))
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
show-addresses-for-permissions (rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :addresses-for-permissions
{:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content
addresses-for-permissions/view}])))
[can-edit-addresses?])
show-airdrop-addresses (rn/use-callback
(fn []
(if can-edit-addresses?
(rf/dispatch [:open-modal :address-for-airdrop
{:community-id id}])
(rf/dispatch [:show-bottom-sheet
{:community-id id
:content airdrop-addresses/view}])))
[can-edit-addresses?])
open-permission-sheet (rn/use-callback
(fn []
(rf/dispatch
[:show-bottom-sheet
{:content (fn []
[permissions-sheet/view id])}]))
[id])]
(rn/use-mount
(fn []
(rf/dispatch [:communities/initialize-permission-addresses id])))
@@ -141,11 +161,4 @@
(i18n/label :t/community-rules)])
(when-not can-edit-addresses?
[community-rules/view id])]]
(when-not can-edit-addresses?
[rn/view {:style (style/bottom-actions theme)}
[quo/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-request-to-join)
:track-icon :i/face-id
:customization-color color
:on-complete confirm-choices}]])]))
[bottom-authentication {:id id}]]))
@@ -190,19 +190,12 @@
(fn []
(if can-edit-addresses?
(do
(rf/dispatch
[:standard-auth/authorize
{:auth-button-label (i18n/label :t/confirm-changes)
:on-auth-success (fn [password]
(rf/dispatch
[:communities/edit-shared-addresses
{:community-id id
:password password
:addresses addresses-to-reveal
:on-success (fn []
(rf/dispatch [:dismiss-modal
:addresses-for-permissions])
(rf/dispatch [:hide-bottom-sheet]))}]))}])
(rf/dispatch [:communities/sign-shared-addresses
{:community-id id
:addresses addresses-to-reveal
:on-success (fn []
(rf/dispatch [:dismiss-modal :addresses-for-permissions])
(rf/dispatch [:hide-bottom-sheet]))}])
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses]))
(do
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses])
@@ -19,20 +19,15 @@
(fn []
(if can-edit-addresses?
(rf/dispatch
[:standard-auth/authorize
{:auth-button-label (i18n/label :t/confirm-changes)
:on-auth-success (fn [password]
(rf/dispatch
[:communities/edit-shared-addresses
{:community-id community-id
:password password
:airdrop-address address
:on-success (fn []
(rf/dispatch
[:dismiss-modal
:address-for-airdrop])
(rf/dispatch
[:hide-bottom-sheet]))}]))}])
[:communities/sign-shared-addresses
{:community-id community-id
:airdrop-address address
:on-success (fn []
(rf/dispatch
[:dismiss-modal
:address-for-airdrop])
(rf/dispatch
[:hide-bottom-sheet]))}])
(do
(rf/dispatch [:communities/set-airdrop-address community-id address])
(rf/dispatch [:hide-bottom-sheet])))))]
@@ -1,41 +0,0 @@
(ns status-im.contexts.communities.actions.request-to-join.style
(:require
[quo.foundations.colors :as colors]))
(def page-container
{:margin-horizontal 20
:margin-bottom 20})
(def title-container
{:flex-direction :row
:align-items :center
:justify-content :space-between})
(def container
{:flex 1})
(def community-icon
{:margin-right :auto :margin-top 4})
(def cancel-button
{:flex 1
:margin-right 12})
(def bottom-container
{:padding-top 32
:flex-direction :row
:align-items :center
:margin-horizontal 20
:justify-content :space-evenly})
(def final-disclaimer-container
{:margin-bottom 7
:margin-top 12
:padding-horizontal 40})
(def final-disclaimer-text
{:color colors/neutral-50
:text-align :center})
(def rules-text
{:margin-top 24})
@@ -1,76 +0,0 @@
(ns status-im.contexts.communities.actions.request-to-join.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[react-native.gesture :as gesture]
[status-im.contexts.communities.actions.community-rules-list.view :as community-rules]
[status-im.contexts.communities.actions.request-to-join.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn join-community-and-navigate-back
[id]
(rf/dispatch
[:standard-auth/authorize
{:auth-button-label (i18n/label :t/request-to-join)
:on-auth-success (fn [password]
(rf/dispatch
[:communities/request-to-join
{:community-id id :password password}]))}])
(rf/dispatch [:navigate-back]))
(defn view
[]
(fn []
(let [theme (quo.theme/use-theme)
{:keys [id]} (rf/sub [:get-screen-params])
{:keys [color name images]} (rf/sub [:communities/community id])
keycard? (rf/sub [:keycard/keycard-profile?])
keycard-feature-unavailable (rn/use-callback
#(rf/dispatch [:keycard/feature-unavailable-show
{:feature-name :community.request-to-join}]))]
[rn/safe-area-view {:flex 1}
[gesture/scroll-view {:style style/container}
[rn/view style/page-container
[rn/view {:style style/title-container}
[quo/text
{:accessibility-label :communities-join-community
:weight :semi-bold
:size :heading-2}
(i18n/label :t/request-to-join)]]
[rn/view {:style style/community-icon}
[quo/context-tag
{:type :community
:size 24
:community-logo (:thumbnail images)
:community-name name}]]
[quo/text
{:style style/rules-text
:accessibility-label :communities-rules-title
:weight :semi-bold
:size :paragraph-1}
(i18n/label :t/community-rules)]
[community-rules/view community-rules/standard-rules false]]]
[rn/view {:style style/bottom-container}
[quo/button
{:accessibility-label :cancel
:on-press #(rf/dispatch [:navigate-back])
:type :grey
:container-style style/cancel-button}
(i18n/label :t/cancel)]
[quo/button
{:accessibility-label :join-community-button
:on-press (if keycard?
keycard-feature-unavailable
#(join-community-and-navigate-back id))
:container-style {:flex 1}
:inner-style {:background-color (colors/resolve-color color theme)}}
(i18n/label :t/request-to-join)]]
[rn/view {:style style/final-disclaimer-container}
[quo/text
{:size :paragraph-2
:style style/final-disclaimer-text}
(i18n/label :t/request-to-join-disclaimer)]]])))
@@ -3,8 +3,7 @@
[status-im.contexts.communities.utils :as utils]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
[utils.re-frame :as rf]))
(rf/reg-event-fx :communities/check-permissions-to-join-community-success
(fn [{:keys [db]} [community-id based-on-client-selection? result]]
@@ -70,32 +69,6 @@
community-id
err))}]})))
(defn request-to-join
[{:keys [db]}
[{:keys [community-id password]}]]
(let [pub-key (get-in db [:profile/profile :public-key])]
{:fx [[:json-rpc/call
[{:method "wakuext_generateJoiningCommunityRequestsForSigning"
:params [pub-key community-id []]
:on-success [:communities/sign-data community-id password]
:on-error [:communities/requested-to-join-error community-id]}]]]}))
;; Event to be called to request to join a community.
;; This event will generate the data to be signed and then call the sign-data event.
;; This is the only event that should be called from the UI.
(rf/reg-event-fx :communities/request-to-join request-to-join)
(defn sign-data
[_ [community-id password sign-params]]
(let [addresses-to-reveal (map :account sign-params)]
{:fx [[:json-rpc/call
[{:method "wakuext_signData"
:params [(map #(assoc % :password (security/safe-unmask-data password)) sign-params)]
:on-success [:communities/request-to-join-with-signatures community-id addresses-to-reveal]
:on-error [:communities/requested-to-join-error community-id]}]]]}))
(rf/reg-event-fx :communities/sign-data sign-data)
(rf/reg-event-fx :communities/requested-to-join-error
(fn [{:keys [db]} [community-id error]]
(log/error "failed to request to join community"
@@ -116,21 +89,6 @@
:t/requested-to-join-community
{:community community-name})}]]]})))
(defn request-to-join-with-signatures
[_ [community-id addresses-to-reveal signatures share-future-addresses?]]
{:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:shareFutureAddresses share-future-addresses?
:airdropAddress (first addresses-to-reveal)}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]]})
(rf/reg-event-fx :communities/request-to-join-with-signatures request-to-join-with-signatures)
(rf/reg-event-fx :communities/toggled-collapsed-category-success
(fn [{:keys [db]} [community-id category-id collapsed?]]
{:db (assoc-in db [:communities/collapsed-categories community-id category-id] collapsed?)}))
@@ -151,21 +109,42 @@
:category-id category-id
:collapse? collapse?})}]}))
(defn request-to-join-with-addresses
[{:keys [db]}
[{:keys [community-id password]}]]
(let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id])
share-future-addresses? (get-in db [:communities/selected-share-all-addresses community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])]
(defn generate-requests-for-signing
[{:keys [db]} [community-id]]
(let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id])]
{:fx [[:effects.community/generate-requests-for-signing
{:pub-key pub-key
:community-id community-id
:addresses-to-reveal addresses-to-reveal
:on-success #(rf/dispatch [:communities/on-join-requests-prepared
community-id
%])}]]}))
(rf/reg-event-fx :communities/generate-requests-for-signing generate-requests-for-signing)
(defn on-join-requests-prepared
[{:keys [db]} [community-id signing-requests]]
{:db (assoc-in db
[:communities/join-requests-for-signing community-id]
signing-requests)})
(rf/reg-event-fx :communities/on-join-requests-prepared on-join-requests-prepared)
(defn request-to-join
[{:keys [db]} [{:keys [community-id signature-data]}]]
(let [share-future-addresses? (get-in db [:communities/selected-share-all-addresses community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])
addresses-to-reveal (map :address signature-data)
signatures (utils/extract-join-request-signatures signature-data)]
{:fx [[:effects.community/request-to-join
{:community-id community-id
:password password
:pub-key pub-key
:signatures signatures
:addresses-to-reveal addresses-to-reveal
:airdrop-address airdrop-address
:share-future-addresses? share-future-addresses?
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]}))
:on-success #(rf/dispatch [:communities/requested-to-join %])
:on-error #(rf/dispatch [:communities/requested-to-join-error community-id
%])}]]}))
(rf/reg-event-fx :communities/request-to-join-with-addresses request-to-join-with-addresses)
(rf/reg-event-fx :communities/request-to-join request-to-join)
@@ -1,58 +0,0 @@
(ns status-im.contexts.communities.overview.events-test
(:require [cljs.test :refer [deftest is]]
matcher-combinators.test
[native-module.core :as native-module]
[status-im.contexts.communities.overview.events :as sut]))
(def password (native-module/sha3 "password123"))
(def community-id "0x99")
(def account-pub-key "0x1")
(deftest request-to-join-test
(let [cofx {:db {:profile/profile {:public-key account-pub-key}}}
expected {:fx [[:json-rpc/call
[{:method "wakuext_generateJoiningCommunityRequestsForSigning"
:params [account-pub-key community-id []]
:on-success [:communities/sign-data community-id password]
:on-error [:communities/requested-to-join-error community-id]}]]]}]
(is (match? expected
(sut/request-to-join cofx
[{:community-id community-id
:password password}])))))
(deftest sign-data-test
(let [cofx {:db {}}
sign-params [{:data "123" :account account-pub-key}
{:data "456" :account "0x2"}]
addresses-to-reveal [account-pub-key "0x2"]
expected {:fx
[[:json-rpc/call
[{:method "wakuext_signData"
:params [[{:data "123" :account account-pub-key :password password}
{:data "456" :account "0x2" :password password}]]
:on-success [:communities/request-to-join-with-signatures
community-id addresses-to-reveal]
:on-error [:communities/requested-to-join-error community-id]}]]]}]
(is (match? expected
(sut/sign-data cofx [community-id password sign-params])))))
(deftest request-to-join-with-signatures-test
(let [cofx {:db {}}
addresses-to-reveal [account-pub-key "0x2"]
share-future-addresses? true
signatures ["11111" "222222"]
expected {:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id
:signatures signatures
:addressesToReveal addresses-to-reveal
:shareFutureAddresses share-future-addresses?
:airdropAddress "0x1"}]
:js-response true
:on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error
community-id]}]]]}]
(is (match? expected
(sut/request-to-join-with-signatures cofx
[community-id addresses-to-reveal signatures
share-future-addresses?])))))
@@ -11,7 +11,6 @@
[status-im.common.resources :as resources]
[status-im.common.scroll-page.style :as scroll-page.style]
[status-im.common.scroll-page.view :as scroll-page]
[status-im.config :as config]
[status-im.constants :as constants]
[status-im.contexts.communities.actions.chat.view :as chat-actions]
[status-im.contexts.communities.actions.community-options.view :as options]
@@ -106,14 +105,10 @@
[quo/text (i18n/label :t/network-not-supported)])
(defn- request-access-button
[id color keycard? keycard-feature-unavailable]
[id color]
[quo/button
{:on-press (if keycard?
keycard-feature-unavailable
(if config/community-accounts-selection-enabled?
#(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
#(rf/dispatch [:open-modal :community-requests-to-join {:id id}])))
{:on-press #(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
:accessibility-label :show-request-to-join-screen-button
:customization-color color
:container-style {:margin-bottom 12}
@@ -131,31 +126,24 @@
(let
[{:keys [can-request-access? no-member-permission? networks-not-supported?
highest-permission-role
tokens]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label
(communities.utils/role->translation-key highest-permission-role
:t/member))
on-press (rn/use-callback
(fn []
(if config/community-accounts-selection-enabled?
(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}])
(rf/dispatch [:open-modal :community-requests-to-join
{:id id}])))
[id])
on-press-info #(rf/dispatch
[:show-bottom-sheet {:content token-gated-communities-info}])
keycard? (rf/sub [:keycard/keycard-profile?])
keycard-feature-unavailable (rn/use-callback
#(rf/dispatch [:keycard/feature-unavailable-show
{:feature-name :community.request-to-join}]))]
tokens]} (rf/sub [:community/token-gated-overview id])
highest-role-text (i18n/label
(communities.utils/role->translation-key highest-permission-role
:t/member))
on-press (rn/use-callback
(fn []
(rf/dispatch [:open-modal :community-account-selection-sheet
{:community-id id}]))
[id])
on-press-info #(rf/dispatch
[:show-bottom-sheet {:content token-gated-communities-info}])]
(cond
networks-not-supported?
[network-not-supported]
(or (not role-permissions?) no-member-permission?)
[request-access-button id color keycard? keycard-feature-unavailable]
[request-access-button id color]
:else
[quo/community-token-gating
@@ -163,7 +151,7 @@
:tokens tokens
:community-color color
:satisfied? can-request-access?
:on-press (if keycard? keycard-feature-unavailable on-press)
:on-press on-press
:on-press-info on-press-info}])))
(defn- join-community
+19 -1
View File
@@ -1,6 +1,8 @@
(ns status-im.contexts.communities.utils
(:require
[status-im.constants :as constants]))
[schema.core :as schema]
[status-im.constants :as constants]
[utils.hex :as hex]))
(defn role->translation-key
([role] (role->translation-key role nil))
@@ -19,3 +21,19 @@
(remove :watch-only?)
(filter :operable?)
(sort-by :position)))
(defn extract-join-request-signatures
[signature-data]
(->> signature-data
(map :signature)
(map hex/prefix-hex)))
(schema/=> extract-join-request-signatures
[:=>
[:cat
[:sequential
[:map {:closed true}
[:signature string?]
[:address string?]
[:message string?]]]]
[:sequential string?]])
+11 -6
View File
@@ -1,4 +1,5 @@
(ns status-im.contexts.keycard.effects
(:require-macros [status-im.common.promesa.core :as common.promesa])
(:require [keycard.keycard :as keycard]
[native-module.core :as native-module]
[promesa.core :as promesa]
@@ -66,16 +67,20 @@
(promesa/then (keycard.utils/get-on-success args))
(promesa/catch (keycard.utils/get-on-failure args)))))
(rf/reg-fx :effects.keycard/sign-hashes
(fn [{:keys [hashes pin path on-success] :as args}]
(-> (promesa/all
(for [hash-data hashes]
(rf/reg-fx :effects.keycard/sign-messages
(fn [{:keys [messages-data pin on-success] :as args}]
;; NOTE: we can't run parallel operations on the keycard (rejects with an error), so we should
;; execute the signings sequentially. Unfortunately, `promesa` has no built-in function that
;; executes promises sequentially which also returns all the results, hence this macro.
(-> (common.promesa/all-seq
(for [{:keys [path message address]} messages-data]
(-> (keycard/sign {:pin pin
:path path
:hash-data (hex/normalize-hex hash-data)})
:hash-data (hex/normalize-hex message)})
(promesa/then (fn [signature]
{:signature signature
:message hash-data})))))
:address address
:message message})))))
(promesa/then on-success)
(promesa/catch (keycard.utils/get-on-failure args)))))
+29 -14
View File
@@ -2,24 +2,39 @@
(:require [utils.address]
[utils.re-frame :as rf]))
(rf/reg-event-fx :keycard/sign-hashes
(fn [_ [data]]
{:effects.keycard/sign-hashes data}))
(defn- append-account-path
[db message-data]
(let [accounts (get-in db [:wallet :accounts])]
(->> (:address message-data)
(get accounts)
:path
(assoc message-data :path))))
(rf/reg-event-fx :keycard/connect-and-sign-hashes
(fn [{:keys [db]} [{:keys [keycard-pin address hashes on-success on-failure]}]]
(let [{:keys [path key-uid]} (get-in db [:wallet :accounts address])]
(defn- add-paths-to-signing-data
[db signing-data]
(reduce (fn [acc message-data]
(conj acc (append-account-path db message-data)))
[]
signing-data))
(rf/reg-event-fx :keycard/sign-messages
(fn [_ [data]]
{:effects.keycard/sign-messages data}))
(rf/reg-event-fx
:keycard/connect-and-sign-messages
(fn [{:keys [db]} [{:keys [keycard-pin messages on-success on-failure]}]]
(let [key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/sign-hashes
{:pin keycard-pin
:path path
:hashes hashes
:on-success (fn [signatures]
(rf/dispatch [:keycard/disconnect])
(when on-success (on-success signatures)))
:on-failure on-failure}]))}]]]})))
[:keycard/sign-messages
{:pin keycard-pin
:messages-data (add-paths-to-signing-data db messages)
:on-success (fn [signatures]
(rf/dispatch [:keycard/disconnect])
(when on-success (on-success signatures)))
:on-failure on-failure}]))}]]]})))
@@ -16,6 +16,7 @@
(rf/dispatch
[:standard-auth/authorize-with-password
{:blur? true
:skip-biometric? true
:theme theme
:auth-button-label (i18n/label :t/biometric-enable-button {:bio-type-label button-label})
:on-auth-success (fn [password]
@@ -146,6 +146,7 @@
{:fx [[:dispatch
[:standard-auth/authorize-with-password
{:blur? true
:skip-biometric? true
:theme :dark
:auth-button-label (i18n/label :t/confirm)
:on-auth-success (fn [password]
@@ -135,6 +135,12 @@
:nativeCurrencySymbol :native-currency-symbol
:nativeCurrencyName :native-currency-symbol})))
(defn partially-operable-accounts?
[accounts]
(->> accounts
(some #(= :partially (:operable %)))
boolean))
(defn get-keypair-lowest-operability
[{:keys [accounts]}]
(cond
+9 -8
View File
@@ -105,12 +105,13 @@
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(defn sign-transaction-hashes
[hashes address password]
(defn sign-messages
[messages password]
(-> (promesa/all
(for [h hashes]
(promesa/let [signature (wallet-rpc/sign-message h address password)]
{:message h
(for [{:keys [message address]} messages]
(promesa/let [signature (wallet-rpc/sign-message message address password)]
{:message message
:address address
:signature signature})))
(promesa/catch (fn [err]
(throw (ex-info "Failed to sign transaction hashes"
@@ -118,9 +119,9 @@
:code :error/sign-transaction-hashes}))))))
(rf/reg-fx
:effects.wallet/sign-transaction-hashes
(fn [{:keys [hashes address password on-success on-error]}]
(-> (sign-transaction-hashes hashes address password)
:effects.wallet/sign-messages
(fn [{:keys [messages password on-success on-error]}]
(-> (sign-messages messages (security/safe-unmask-data password))
(promesa/then on-success)
(promesa/catch on-error))))
+8 -34
View File
@@ -666,40 +666,14 @@
:text (:message error)}]))}]}))
(rf/reg-event-fx
:wallet/prepare-signatures-for-transactions
(fn [{:keys [db]} [type sha3-pwd]]
(let [{:keys [hashes address signOnKeycard]} (get-in db
[:wallet :ui type :transaction-for-signing
:signingDetails])
on-success (fn [signatures]
(rf/dispatch
[:wallet/send-router-transactions-with-signatures type
signatures]))
on-error (fn [error]
(log/error
"failed to prepare signatures for transactions"
{:event :wallet/prepare-signatures-for-transactions
:error error})
(rf/dispatch
[:toasts/upsert
{:id :prepare-signatures-for-transactions-error
:type :negative
:text (:message error)}]))]
(if signOnKeycard
{:fx [[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete #(rf/dispatch [:keycard/connect-and-sign-hashes
{:keycard-pin %
:address address
:hashes hashes
:on-success on-success
:on-failure on-error}])}]]]}
{:fx [[:effects.wallet/sign-transaction-hashes
{:hashes hashes
:address address
:password (security/safe-unmask-data sha3-pwd)
:on-success on-success
:on-error on-error}]]}))))
:wallet/on-sign-transaction-error
(fn [_ [error]]
(log/error
"failed to sign transaction"
{:error error})
{:fx [[:toasts/upsert
{:type :negative
:text (:message error)}]]}))
(rf/reg-event-fx
:wallet/send-router-transactions-with-signatures
@@ -248,9 +248,7 @@
account))}
user-props {:full-name to-address
:address (utils/get-shortened-address
to-address)}
sign-on-keycard? (get-in transaction-for-signing
[:signingDetails :signOnKeycard])]
to-address)}]
(hot-reload/use-safe-unmount #(rf/dispatch [:wallet/clean-route-data-for-collectible-tx]))
;; In token send flow we already have transaction built when
;; we reach confirmation screen. But in send collectible flow
@@ -286,7 +284,9 @@
:transaction-type transaction-type}]
(when (and (not loading-suggested-routes?) route (seq route))
[standard-auth/slide-button
{:size :size-48
{:sign-payload (standard-auth/transaction-payload
transaction-for-signing)
:size :size-48
:track-text (if (= transaction-type :tx/bridge)
(i18n/label :t/slide-to-bridge)
(i18n/label :t/slide-to-send))
@@ -294,16 +294,14 @@
:disabled? (not transaction-for-signing)
:customization-color account-color
:auth-button-label (i18n/label :t/confirm)
:on-complete (when sign-on-keycard?
#(rf/dispatch
[:wallet/prepare-signatures-for-transactions
:send
""]))
:on-auth-fail (fn [error]
(rf/dispatch [:wallet/on-sign-transaction-error
error]))
:on-auth-success
(fn [psw]
(rf/dispatch
[:wallet/prepare-signatures-for-transactions :send
psw]))}])]
(fn [signatures]
(rf/dispatch [:wallet/send-router-transactions-with-signatures
:send
signatures]))}])]
:gradient-cover? true
:customization-color (:color account)}
[rn/view
@@ -220,20 +220,24 @@
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
transaction-for-signing (rf/sub [:wallet/swap-transaction-for-signing])
sign-on-keycard? (get-in transaction-for-signing
[:signingDetails :signOnKeycard])
on-auth-fail (rn/use-callback
(fn [error]
(rf/dispatch [:wallet/on-sign-transaction-error
error])))
on-auth-success (rn/use-callback
#(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap %]))
on-complete (rn/use-callback
#(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap ""]))]
(fn [signatures]
(rf/dispatch [:wallet/send-router-transactions-with-signatures
:swap
signatures])))]
[standard-auth/slide-button
{:size :size-48
{:sign-payload (standard-auth/transaction-payload transaction-for-signing)
:size :size-48
:track-text (i18n/label :t/slide-to-sign)
:container-style {:z-index 2}
:customization-color (:color account)
:disabled? (or loading-swap-proposal? (not swap-proposal))
:on-complete (when sign-on-keycard? on-complete)
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label (i18n/label :t/confirm)}]))
(defn- footer
@@ -168,10 +168,19 @@
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
account-color (:color account)
sign-on-keycard? (get-in transaction-for-signing
[:signingDetails :signOnKeycard])]
on-auth-fail (rn/use-callback
(fn [error]
(rf/dispatch [:wallet/on-sign-transaction-error
error])))
on-auth-success (rn/use-callback
(fn [signatures]
(rf/dispatch [:wallet/stop-get-swap-proposal])
(rf/dispatch [:wallet/send-router-transactions-with-signatures
:swap
signatures])))]
[standard-auth/slide-button
{:size :size-48
{:sign-payload (standard-auth/transaction-payload transaction-for-signing)
:size :size-48
:track-text (i18n/label :t/slide-to-swap)
:container-style {:z-index 2}
:customization-color account-color
@@ -179,14 +188,8 @@
(not swap-proposal)
(not transaction-for-signing))
:auth-button-label (i18n/label :t/confirm)
:on-complete (when sign-on-keycard?
#(rf/dispatch
[:wallet/prepare-signatures-for-transactions
:swap
""]))
:on-auth-success (fn [data]
(rf/dispatch [:wallet/stop-get-swap-proposal])
(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap data]))}]))
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail}]))
(defn footer
[]
@@ -2,44 +2,13 @@
(: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/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]]
@@ -6,20 +6,44 @@
[status-im.common.standard-authentication.core :as standard-authentication]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.wallet-connect.modals.common.footer.style :as style]
[utils.hex :as hex]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- on-auth-success
[password]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet-connect/authorized-signing password]))
[signatures]
(let [signature (-> signatures
first
:signature
hex/prefix-hex)]
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:wallet-connect/respond signature])))
(defn- on-auth-fail
[error]
(rf/dispatch [:wallet-connect/on-sign-error error]))
(defn- slide-button
[{:keys [disabled? slide-button-text]}]
(let [{:keys [customization-color]} (rf/sub [:wallet-connect/current-request-account-details])
address (rf/sub [:wallet-connect/current-request-address])
prepared-hash (rf/sub [:wallet-connect/prepared-hash])]
[rn/view {:style style/auth-container}
[standard-authentication/slide-button
{:sign-payload [{:address address
:message prepared-hash}]
:size :size-48
:track-text slide-button-text
:disabled? (and (not prepared-hash) disabled?)
:customization-color customization-color
:on-auth-success on-auth-success
:on-auth-fail on-auth-fail
:auth-button-label (i18n/label :t/confirm)}]]))
(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)
sign-on-keycard? (rf/sub [:wallet-connect/sign-on-keycard?])]
(let [offline? (rf/sub [:network/offline?])
theme (quo.theme/use-theme)]
[:<>
(when (or offline? error-state)
[quo/alert-banner
@@ -42,16 +66,9 @@
(into [rn/view
{:style style/data-items-container}]
children)
[rn/view {:style style/auth-container}
[standard-authentication/slide-button
{:size :size-48
:track-text slide-button-text
: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)}]]
[slide-button
{:disabled? (or offline? error-state)
:slide-button-text slide-button-text}]
[rn/view {:style style/warning-container}
[quo/text
{:size :paragraph-2
-7
View File
@@ -25,7 +25,6 @@
[status-im.contexts.communities.actions.channel-view-details.view :as
channel-view-channel-members-and-details]
[status-im.contexts.communities.actions.invite-contacts.view :as communities.invite]
[status-im.contexts.communities.actions.request-to-join.view :as join-menu]
[status-im.contexts.communities.actions.share-community-channel.view :as share-community-channel]
[status-im.contexts.communities.actions.share-community.view :as share-community]
[status-im.contexts.communities.discover.view :as communities.discover]
@@ -230,12 +229,6 @@
:options {:insets {:top? true}}
:component communities.accounts-selection/view}
{:name :community-requests-to-join
:metrics {:track? true
:alias-id :community.request-to-join}
:options {:sheet? true}
:component join-menu/view}
{:name :screen/share-community
:metrics {:track? true
:alias-id :community.share-community}
+6
View File
@@ -479,3 +479,9 @@
(fn [[permissions] _]
(let [all-tokens (apply concat (map :tokens permissions))]
(boolean (some seq all-tokens)))))
(re-frame/reg-sub
:communities/join-requests-for-signing-by-id
:<- [:communities/join-requests-for-signing]
(fn [sign-requests [_ id]]
(get sign-requests id)))
+2
View File
@@ -17,6 +17,7 @@
status-im.subs.profile
status-im.subs.settings
status-im.subs.shell
status-im.subs.standard-authentication
status-im.subs.wallet.activities
status-im.subs.wallet.buy
status-im.subs.wallet.collectibles
@@ -159,6 +160,7 @@
(reg-root-key-sub :communities/permissions-checks-for-selection
:communities/permissions-checks-for-selection)
(reg-root-key-sub :communities/channel-permissions-check :communities/channel-permissions-check)
(reg-root-key-sub :communities/join-requests-for-signing :communities/join-requests-for-signing)
;;activity center
(reg-root-key-sub :activity-center :activity-center)
@@ -0,0 +1,19 @@
(ns status-im.subs.standard-authentication
(:require [status-im.common.biometric.utils :as biometric]
[status-im.constants :as constants]
[utils.re-frame :as rf]))
(rf/reg-sub
:standard-auth/slider-icon
:<- [:auth-method]
:<- [:biometrics/supported-type]
:<- [:keycard/keycard-profile?]
(fn [[auth-method biometrics-type keycard-pairing?]]
(let [icons {:biometrics (biometric/get-icon-by-type biometrics-type)
:password :password
:keycard :i/keycard}
auth-with-biometrics? (= auth-method constants/auth-method-biometric)]
(cond
keycard-pairing? (:keycard icons)
auth-with-biometrics? (:biometrics icons)
:else (:password icons)))))
@@ -13,6 +13,12 @@
:<- [:wallet-connect/current-request]
:-> :address)
(rf/reg-sub
:wallet-connect/prepared-hash
:<- [:wallet-connect/current-request]
(fn [request]
(:prepared-hash request)))
(rf/reg-sub
:wallet-connect/current-request-display-data
:<- [:wallet-connect/current-request]
+2 -3
View File
@@ -5,6 +5,7 @@
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.missing-keypair.view :as missing-keypair]
[status-im.subs.wallet.add-account.address-to-watch]
@@ -865,9 +866,7 @@
:wallet/has-partially-operable-accounts?
:<- [:wallet/accounts]
(fn [accounts]
(->> accounts
(some #(= :partially (:operable %)))
boolean)))
(data-store/partially-operable-accounts? accounts)))
(rf/reg-sub
:wallet/accounts-names