Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6163c1b0a5 | ||
|
|
cbe5f5cfaf | ||
|
|
c2a2eeec59 | ||
|
|
9551b36976 | ||
|
|
35fbc5ccb4 | ||
|
|
2ea2f945de | ||
|
|
5f89593a49 | ||
|
|
baa44471cf | ||
|
|
38794d81ae |
@@ -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)))
|
||||
@@ -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
|
||||
|
||||
@@ -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?]])
|
||||
|
||||
@@ -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)))))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user